diff --git a/docs/regs/hyperbus_cfg_regs.md b/docs/regs/hyperbus_cfg_regs.md
index 9458acf..a7c120e 100644
--- a/docs/regs/hyperbus_cfg_regs.md
+++ b/docs/regs/hyperbus_cfg_regs.md
@@ -334,7 +334,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-
Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -440,7 +440,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -546,7 +546,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -652,7 +652,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -758,7 +758,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -864,7 +864,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -970,7 +970,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
@@ -1076,7 +1076,7 @@ Don't override. Generated from: hyperbus_cfg_regs
- Base Offset: 0xC
- Size: 0x4
-Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
+Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.
|Bits| Identifier |Access|Reset|Name|
|----|---------------------|------|-----|----|
diff --git a/src/backend/hyperbus_backend.sv b/src/backend/hyperbus_backend.sv
index ac3e4d2..4b6cd64 100644
--- a/src/backend/hyperbus_backend.sv
+++ b/src/backend/hyperbus_backend.sv
@@ -49,7 +49,7 @@ module hyperbus_backend #(
hyperbus_pkg::phy_cfg_t cfg_q;
logic cfg_apply_accepted;
logic phy_busy_any;
- logic clk_tx;
+ logic [NumPhys-1:0] clk_tx;
`ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2)
`ASSERT_INIT(SyncStagesValid, SyncStages >= 2)
@@ -60,16 +60,18 @@ module hyperbus_backend #(
`FFLARN(cfg_q, cfg_apply_i, cfg_apply_accepted, '0, clk_i, rst_ni)
- hyperbus_tx_clk_delay i_tx_clk_delay (
- .rst_ni,
+ for (genvar i = 0; i < NumPhys; i++) begin : gen_tx_clk_delay
+ hyperbus_tx_clk_delay i_tx_clk_delay (
+ .rst_ni,
`ifdef TARGET_XILINX
- .clk_ref200_i,
+ .clk_ref200_i,
`endif
- .clk_i,
- .in_i ( clk_i ),
- .delay_i ( cfg_q.t_tx_clk_delay ),
- .out_o ( clk_tx )
- );
+ .clk_i,
+ .in_i ( clk_i ),
+ .delay_i ( cfg_q.phy[i].tx_delay ),
+ .out_o ( clk_tx[i] )
+ );
+ end
/////////////////////
// Physical lanes //
@@ -177,10 +179,11 @@ module hyperbus_backend #(
hyperbus_phy #(
.StartupCycles ( StartupCycles ),
.NumPhys ( NumPhys ),
- .SyncStages ( SyncStages )
+ .SyncStages ( SyncStages ),
+ .PhyIndex ( i )
) i_phy (
.clk_i,
- .clk_tx_i ( clk_tx ),
+ .clk_tx_i ( clk_tx[i] ),
.rst_ni,
.test_mode_i,
.cfg_i ( cfg_q ),
@@ -218,10 +221,11 @@ module hyperbus_backend #(
hyperbus_phy #(
.StartupCycles ( StartupCycles ),
.NumPhys ( NumPhys ),
- .SyncStages ( SyncStages )
+ .SyncStages ( SyncStages ),
+ .PhyIndex ( 0 )
) i_phy (
.clk_i,
- .clk_tx_i ( clk_tx ),
+ .clk_tx_i ( clk_tx[0] ),
.rst_ni,
.test_mode_i,
.cfg_i ( cfg_q ),
diff --git a/src/backend/hyperbus_phy.sv b/src/backend/hyperbus_phy.sv
index 031a830..02d25d7 100644
--- a/src/backend/hyperbus_phy.sv
+++ b/src/backend/hyperbus_phy.sv
@@ -14,6 +14,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
parameter int unsigned TimerWidth = 16,
parameter int unsigned RxFifoLogDepth = 3,
parameter int unsigned SyncStages = 2,
+ parameter int unsigned PhyIndex = 0,
// Conservative startup delay: 300 us at 200 MHz.
parameter int unsigned StartupCycles = 300 * 200
)(
@@ -67,9 +68,9 @@ module hyperbus_phy import hyperbus_pkg::*; #(
localparam int unsigned RxOutstandingLimit = (RxFifoDepth > RxFifoStopMargin) ?
(RxFifoDepth - RxFifoStopMargin) : 1;
- logic [1:0] words_per_beat;
+ `ASSERT_INIT(PhyIndexValid, PhyIndex < 2)
- assign words_per_beat = (NumPhys == 2 && cfg_i.dual_phy) ? 2 : 1;
+ logic [1:0] words_per_beat;
//////////////////////
// Persistent state //
@@ -80,6 +81,22 @@ module hyperbus_phy import hyperbus_pkg::*; #(
hyper_tf_t tf_d, tf_q;
logic [HyperNumChips-1:0] cs_d, cs_q;
logic add_latency_d, add_latency_q;
+ logic [HyperNumChips-1:0] cfg_select_cs;
+ logic [2:0] cfg_chip_idx;
+ chip_phy_cfg_t cfg_chip;
+
+ // During Idle the incoming command CS is authoritative. Once accepted,
+ // retain the registered CS for every subsequent phase of the transfer.
+ assign cfg_select_cs = (state_q == Idle) ? trans_cs_i : cs_q;
+ onehot_to_bin #(
+ .ONEHOT_WIDTH ( HyperNumChips )
+ ) i_cfg_chip_idx (
+ .onehot ( cfg_select_cs ),
+ .bin ( cfg_chip_idx )
+ );
+ assign cfg_chip = cfg_i.chip[cfg_chip_idx];
+
+ assign words_per_beat = (NumPhys == 2 && cfg_i.dual_phy) ? 2 : 1;
// Whether B response is pending
logic b_pending_q;
@@ -144,7 +161,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
.tx_data_oe_i ( trx_tx_data_oe ),
.tx_rwds_i ( trx_tx_rwds ),
.tx_rwds_oe_i ( trx_tx_rwds_oe ),
- .rx_clk_delay_i ( cfg_i.chip.t_rx_clk_delay ),
+ .rx_clk_delay_i ( cfg_chip.t_rx_clk_delay ),
.rx_clk_set_i ( trx_rx_clk_set ),
.rx_clk_reset_i ( trx_rx_clk_reset ),
.rx_data_o ( trx_rx_data ),
@@ -189,7 +206,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
trx_tx_data = tx_data_i;
trx_tx_rwds = ~tx_strb_i;
tx_ready_o = 1'b1; // Memory always ready within HyperBus burst
- ctl_wclk_ena = tx_valid_i;
+ ctl_wclk_ena = tx_valid_i;
end
end
@@ -234,8 +251,8 @@ module hyperbus_phy import hyperbus_pkg::*; #(
// Auxiliary control signals
assign ctl_write_zero_lat = tf_q.address_space & tf_q.write;
- // cfg_i.chip.en_latency_additional overwrites the sampled RWDS value.
- assign ctl_add_latency = trx_rwds_sample | cfg_i.chip.en_latency_additional;
+ // The selected chip configuration overwrites the sampled RWDS value.
+ assign ctl_add_latency = trx_rwds_sample | cfg_chip.en_latency_additional;
assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == words_per_beat);
assign ctl_tf_burst_done = (tf_q.burst == 0);
@@ -288,11 +305,11 @@ module hyperbus_phy import hyperbus_pkg::*; #(
cs_d = trans_cs_i;
add_latency_d = 1'b0;
- if(cfg_i.chip.csn_to_ck_cycles != 0) begin
+ if(cfg_chip.csn_to_ck_cycles != 0) begin
// assert CS but delay hyper_ck to allow more time
// for memory to drive RWDS (to satisfy t_DSV)
state_d = DelayCK;
- timer_d = cfg_i.chip.csn_to_ck_cycles -1;
+ timer_d = cfg_chip.csn_to_ck_cycles -1;
end else begin
// max throughput when memory RWDS signal arrives early
state_d = SendCA;
@@ -323,10 +340,10 @@ module hyperbus_phy import hyperbus_pkg::*; #(
trx_rwds_sample_ena = ~ctl_write_zero_lat;
if (ctl_timer_zero) begin
if (ctl_write_zero_lat) begin
- timer_d = cfg_i.chip.t_burst_max;
+ timer_d = cfg_chip.t_burst_max;
state_d = Write;
end else begin
- timer_d = TimerWidth'(cfg_i.chip.t_latency_access);
+ timer_d = TimerWidth'(cfg_chip.t_latency_access);
add_latency_d = ctl_add_latency;
state_d = WaitLatAccess;
end
@@ -344,7 +361,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
if (~add_latency_q) begin
// Substract cycle for last CA and another for state delay
if(ctl_timer_two) begin
- timer_d = cfg_i.chip.t_burst_max;
+ timer_d = cfg_chip.t_burst_max;
// Switch to write or read phase and already start
// turnaround of tri-state driver (depending on latency
// config and if read or write transaction).
@@ -364,7 +381,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
end else if (ctl_timer_one) begin
// instead of going to 0, add another latency count
state_d = WaitAddLatAccess;
- timer_d = TimerWidth'(cfg_i.chip.t_latency_access);
+ timer_d = TimerWidth'(cfg_chip.t_latency_access);
add_latency_d = 1'b0;
end
end
@@ -375,7 +392,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
trx_clk_ena = 1'b1;
trx_tx_data_oe = 1'b1;
if (ctl_timer_two) begin
- timer_d = cfg_i.chip.t_burst_max;
+ timer_d = cfg_chip.t_burst_max;
if (tf_q.write) begin
state_d = Write;
trx_tx_data_oe = 1'b1;
@@ -397,13 +414,13 @@ module hyperbus_phy import hyperbus_pkg::*; #(
tf_d.burst = tf_q.burst - words_per_beat;
tf_d.address = tf_q.address + 1;
if (ctl_tf_burst_last) begin
- timer_d = cfg_i.chip.t_csh_cycles;
+ timer_d = cfg_chip.t_csh_cycles;
state_d = WaitXfer;
end
end
// Force-terminate access on burst time limit
if (ctl_timer_one) begin
- timer_d = cfg_i.chip.t_csh_cycles;
+ timer_d = cfg_chip.t_csh_cycles;
state_d = WaitXfer;
end
end
@@ -420,13 +437,13 @@ module hyperbus_phy import hyperbus_pkg::*; #(
tf_d.address = tf_q.address + 1;
if (ctl_tf_burst_last) begin
b_pending_set = 1'b1;
- timer_d = cfg_i.chip.t_csh_cycles;
+ timer_d = cfg_chip.t_csh_cycles;
state_d = WaitXfer;
end
end
// Force-terminate access on burst time limit
if (ctl_timer_one) begin
- timer_d = cfg_i.chip.t_csh_cycles;
+ timer_d = cfg_chip.t_csh_cycles;
state_d = WaitXfer;
end
end
@@ -435,7 +452,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
// Wait for FFed Clock and output to stop
// May have to be prolonged for potential future devices with t_CSH > 0
if (ctl_timer_zero) begin
- timer_d = cfg_i.chip.t_read_write_recovery;
+ timer_d = cfg_chip.t_read_write_recovery;
state_d = WaitRWR;
end
end
@@ -491,5 +508,7 @@ module hyperbus_phy import hyperbus_pkg::*; #(
`ASSERT(RxCaptureActiveDuringRead, state_q == Read |-> !trx_rx_clk_reset)
`ASSERT(RxCaptureResetAfterDrain,
(r_outstand_dec && r_outstand_q == 1 && state_q != Read) |=> trx_rx_clk_reset)
+ `ASSERT(OutputDriversDisabledDuringRecovery,
+ state_q == WaitRWR |-> (!hyper_dq_oe_o && !hyper_rwds_oe_o))
endmodule
diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv
index 0c0d567..85e6e7e 100644
--- a/src/hyperbus_asynchronous.sv
+++ b/src/hyperbus_asynchronous.sv
@@ -14,6 +14,7 @@ module hyperbus_asynchronous #(
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic,
parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
parameter type reg_req_t = logic,
parameter type reg_rsp_t = logic,
parameter type axi_rule_t = logic,
@@ -127,7 +128,8 @@ module hyperbus_asynchronous #(
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.host_rule_t ( axi_rule_t ),
- .RegDataWidth ( RegDataWidth )
+ .RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth )
) i_cfg_frontend (
.clk_i ( clk_sys_i ),
.rst_ni ( rst_sys_ni ),
diff --git a/src/hyperbus_cfg_frontend.sv b/src/hyperbus_cfg_frontend.sv
index ce7a07e..0607409 100644
--- a/src/hyperbus_cfg_frontend.sv
+++ b/src/hyperbus_cfg_frontend.sv
@@ -6,112 +6,83 @@
`include "common_cells/assertions.svh"
module hyperbus_cfg_frontend #(
- parameter int unsigned NumPhys = 2,
- parameter type reg_req_t = logic,
- parameter type reg_rsp_t = logic,
- parameter type host_rule_t = logic,
- parameter int unsigned RegDataWidth = -1,
- parameter logic [7:0] CapabilityFeatures = 8'b0010_0000
+ parameter int unsigned NumPhys = 2,
+ parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
+ parameter bit ClockDividerImplemented = 1'b0,
+ parameter type reg_req_t = logic,
+ parameter type reg_rsp_t = logic,
+ parameter type host_rule_t = logic
) (
- input logic clk_i,
- input logic rst_ni,
-
- input reg_req_t reg_req_i,
- output reg_rsp_t reg_rsp_o,
-
- output logic drain_o,
- input logic host_idle_i,
- input logic trans_active_i,
-
- output hyperbus_pkg::phy_cfg_t cfg_apply_o,
- output logic cfg_apply_valid_o,
- input logic cfg_apply_ready_i,
- input logic cfg_apply_done_i,
-
- output logic [7:0] clock_div_apply_o,
- output logic clock_div_apply_valid_o,
- input logic clock_div_apply_ready_i,
- input logic clock_div_apply_done_i,
-
+ input logic clk_i,
+ input logic rst_ni,
+ input reg_req_t reg_req_i,
+ output reg_rsp_t reg_rsp_o,
+ output logic drain_o,
+ input logic host_idle_i,
+ input logic trans_active_i,
+ output hyperbus_pkg::phy_cfg_t cfg_apply_o,
+ output logic cfg_apply_valid_o,
+ input logic cfg_apply_ready_i,
+ input logic cfg_apply_done_i,
+ output logic [7:0] clock_div_apply_o,
+ output logic clock_div_apply_valid_o,
+ input logic clock_div_apply_ready_i,
+ input logic clock_div_apply_done_i,
output hyperbus_pkg::frontend_cfg_t frontend_cfg_o,
output host_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o,
- input logic decode_error_i
+ input logic decode_error_i
);
-
- typedef enum logic [2:0] {
+ typedef enum logic [3:0] {
+ CfgInit,
CfgIdle,
CfgDrain,
- CfgCommit,
- CfgObserve,
CfgClockSend,
- CfgClockWaitAck,
+ CfgClockWait,
CfgPhySend,
- CfgPhyWaitAck
+ CfgPhyWait
} cfg_state_e;
- //////////////////////
- // Persistent state //
- //////////////////////
-
- cfg_state_e cfg_state_d;
- cfg_state_e cfg_state_q;
- hyperbus_pkg::phy_cfg_t phy_cfg;
- hyperbus_pkg::phy_cfg_t cfg_applied_d;
- hyperbus_pkg::phy_cfg_t cfg_applied_q;
- logic [7:0] clock_div_applied_d;
- logic [7:0] clock_div_applied_q;
- logic phy_cfg_changed;
- logic clock_div_changed;
- logic cfg_changed;
- logic cfg_write_pending_d;
- logic cfg_write_pending_q;
+ cfg_state_e cfg_state_d, cfg_state_q;
+ hyperbus_pkg::frontend_cfg_t staged_frontend_cfg;
+ hyperbus_pkg::phy_cfg_t staged_phy_cfg;
+ hyperbus_pkg::frontend_cfg_t applied_frontend_d, applied_frontend_q;
+ hyperbus_pkg::phy_cfg_t applied_phy_d, applied_phy_q;
+ hyperbus_pkg::frontend_cfg_t pending_frontend_d, pending_frontend_q;
+ hyperbus_pkg::phy_cfg_t pending_phy_d, pending_phy_q;
+ logic command_flush, command_apply;
+ logic apply_pending_d, apply_pending_q;
+ logic cfg_changed;
+ logic status_access;
+ logic clock_changed;
+ logic phy_changed;
+ logic drain_complete;
`ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2)
`ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32)
-
- //////////////////////////
- // Configuration changes //
- //////////////////////////
-
- logic cfg_write_requested;
- logic drain_completed;
- logic cfg_write_accepted;
- logic clock_apply_accepted;
- logic phy_apply_accepted;
-
- assign phy_cfg_changed = phy_cfg != cfg_applied_q;
- assign clock_div_changed = frontend_cfg_o.phy_clock_div != clock_div_applied_q;
- assign cfg_changed = phy_cfg_changed || clock_div_changed;
- assign drain_o = (cfg_state_q != CfgIdle) || cfg_changed;
- assign cfg_apply_valid_o = cfg_state_q == CfgPhySend;
- assign cfg_apply_o = phy_cfg;
- assign clock_div_apply_valid_o = cfg_state_q == CfgClockSend;
- assign clock_div_apply_o = frontend_cfg_o.phy_clock_div;
- assign cfg_write_requested = reg_req_i.valid && reg_req_i.write;
- assign drain_completed = host_idle_i && !trans_active_i;
- assign cfg_write_accepted = cfg_reg_req.valid && cfg_reg_rsp.ready;
- assign clock_apply_accepted = clock_div_apply_valid_o && clock_div_apply_ready_i;
- assign phy_apply_accepted = cfg_apply_valid_o && cfg_apply_ready_i;
-
- ////////////////////////////
- // Register access gating //
- ////////////////////////////
-
+ `ASSERT_INIT(RegAddrWidthValid, RegAddrWidth >= 12)
+
+ assign cfg_changed = (staged_frontend_cfg != applied_frontend_q) ||
+ (staged_phy_cfg != applied_phy_q);
+ assign clock_changed = staged_frontend_cfg.divider != applied_frontend_q.divider;
+ assign phy_changed = staged_phy_cfg != applied_phy_q;
+ assign drain_complete = host_idle_i && !trans_active_i;
+ assign status_access = reg_req_i.valid &&
+ (reg_req_i.addr == RegAddrWidth'(12'h010));
+
+ assign drain_o = cfg_state_q != CfgIdle;
+ assign cfg_apply_o = pending_phy_q;
+ assign cfg_apply_valid_o = cfg_state_q == CfgPhySend;
+ assign clock_div_apply_o = pending_frontend_q.divider;
+ assign clock_div_apply_valid_o = cfg_state_q == CfgClockSend;
+
+ // STATUS reads and W1C writes stay live while a barrier is in progress.
reg_req_t cfg_reg_req;
reg_rsp_t cfg_reg_rsp;
-
always_comb begin : proc_cfg_reg_gate
cfg_reg_req = reg_req_i;
- reg_rsp_o = cfg_reg_rsp;
-
- if (reg_req_i.write) begin
- cfg_reg_req.valid = reg_req_i.valid && (cfg_state_q == CfgCommit);
- if (cfg_state_q != CfgCommit) begin
- reg_rsp_o.ready = 1'b0;
- reg_rsp_o.error = 1'b0;
- reg_rsp_o.rdata = '0;
- end
- end else if (cfg_state_q != CfgIdle) begin
+ reg_rsp_o = cfg_reg_rsp;
+ if (cfg_state_q != CfgIdle && !status_access) begin
cfg_reg_req.valid = 1'b0;
reg_rsp_o.ready = 1'b0;
reg_rsp_o.error = 1'b0;
@@ -119,119 +90,153 @@ module hyperbus_cfg_frontend #(
end
end
- /////////////////////////////////
- // Atomic configuration update //
- /////////////////////////////////
-
- // Drain host traffic before committing a register write or applying changed
- // clock and PHY values. Each destination acknowledges before traffic resumes.
- always_comb begin : proc_cfg_apply
- cfg_state_d = cfg_state_q;
- cfg_applied_d = cfg_applied_q;
- clock_div_applied_d = clock_div_applied_q;
- cfg_write_pending_d = cfg_write_pending_q;
+ always_comb begin : proc_cfg_fsm
+ cfg_state_d = cfg_state_q;
+ applied_frontend_d = applied_frontend_q;
+ applied_phy_d = applied_phy_q;
+ pending_frontend_d = pending_frontend_q;
+ pending_phy_d = pending_phy_q;
+ apply_pending_d = apply_pending_q;
unique case (cfg_state_q)
- // Detect pending register or applied-configuration changes.
+ CfgInit: begin
+ pending_frontend_d = staged_frontend_cfg;
+ pending_phy_d = staged_phy_cfg;
+ apply_pending_d = 1'b1;
+ if (drain_complete) begin
+ if (ClockDividerImplemented && clock_changed) begin
+ cfg_state_d = CfgClockSend;
+ end else if (phy_changed) begin
+ cfg_state_d = CfgPhySend;
+ end else begin
+ applied_frontend_d = staged_frontend_cfg;
+ applied_phy_d = staged_phy_cfg;
+ apply_pending_d = 1'b0;
+ cfg_state_d = CfgIdle;
+ end
+ end
+ end
CfgIdle: begin
- if (cfg_changed) begin
- cfg_write_pending_d = 1'b0;
+ // APPLY wins if both command bits are written together.
+ if (command_apply) begin
+ pending_frontend_d = staged_frontend_cfg;
+ pending_phy_d = staged_phy_cfg;
+ apply_pending_d = 1'b1;
cfg_state_d = CfgDrain;
- end else if (cfg_write_requested) begin
- cfg_write_pending_d = 1'b1;
+ end else if (command_flush) begin
+ apply_pending_d = 1'b0;
cfg_state_d = CfgDrain;
end
end
- // Stop admission and wait until all accepted traffic has completed.
CfgDrain: begin
- if (drain_completed) begin
- cfg_state_d = cfg_write_pending_q ? CfgCommit : CfgObserve;
- end
- end
- // Commit exactly one blocked register write while traffic remains drained.
- CfgCommit: begin
- if (cfg_write_accepted) begin
- cfg_write_pending_d = 1'b0;
- cfg_state_d = CfgObserve;
+ if (drain_complete) begin
+ if (apply_pending_q) begin
+ if (ClockDividerImplemented &&
+ (pending_frontend_q.divider != applied_frontend_q.divider)) begin
+ cfg_state_d = CfgClockSend;
+ end else if (pending_phy_q != applied_phy_q) begin
+ cfg_state_d = CfgPhySend;
+ end else begin
+ applied_frontend_d = pending_frontend_q;
+ applied_phy_d = pending_phy_q;
+ apply_pending_d = 1'b0;
+ cfg_state_d = CfgIdle;
+ end
+ end else begin
+ cfg_state_d = CfgIdle;
+ end
end
end
- // Re-evaluate generated configuration outputs after the write commits.
- CfgObserve: begin
- if (clock_div_changed) begin
- cfg_state_d = CfgClockSend;
- end else if (phy_cfg_changed) begin
- cfg_state_d = CfgPhySend;
- end else begin
- cfg_state_d = CfgIdle;
- end
- end
- // Send a changed divider value to the isochronous clock generator.
CfgClockSend: begin
- if (clock_apply_accepted && clock_div_apply_done_i) begin
- clock_div_applied_d = frontend_cfg_o.phy_clock_div;
- cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle;
- end else if (clock_apply_accepted) begin
- cfg_state_d = CfgClockWaitAck;
+ if (clock_div_apply_valid_o && clock_div_apply_ready_i) begin
+ if (clock_div_apply_done_i) begin
+ if (pending_phy_q != applied_phy_q) begin
+ cfg_state_d = CfgPhySend;
+ end else begin
+ applied_frontend_d = pending_frontend_q;
+ apply_pending_d = 1'b0;
+ cfg_state_d = CfgIdle;
+ end
+ end else begin
+ cfg_state_d = CfgClockWait;
+ end
end
end
- // Hold the barrier until the divider confirms the update.
- CfgClockWaitAck: begin
+ CfgClockWait: begin
if (clock_div_apply_done_i) begin
- clock_div_applied_d = frontend_cfg_o.phy_clock_div;
- cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle;
+ if (pending_phy_q != applied_phy_q) begin
+ cfg_state_d = CfgPhySend;
+ end else begin
+ applied_frontend_d = pending_frontend_q;
+ apply_pending_d = 1'b0;
+ cfg_state_d = CfgIdle;
+ end
end
end
- // Send changed PHY configuration through the selected bridge.
CfgPhySend: begin
- if (phy_apply_accepted && cfg_apply_done_i) begin
- cfg_applied_d = phy_cfg;
- cfg_state_d = CfgIdle;
- end else if (phy_apply_accepted) begin
- cfg_state_d = CfgPhyWaitAck;
+ if (cfg_apply_valid_o && cfg_apply_ready_i) begin
+ cfg_state_d = cfg_apply_done_i ? CfgIdle : CfgPhyWait;
+ if (cfg_apply_done_i) begin
+ applied_frontend_d = pending_frontend_q;
+ applied_phy_d = pending_phy_q;
+ apply_pending_d = 1'b0;
+ end
end
end
- // Hold the barrier until the backend confirms the PHY update.
- CfgPhyWaitAck: begin
+ CfgPhyWait: begin
if (cfg_apply_done_i) begin
- cfg_applied_d = phy_cfg;
- cfg_state_d = CfgIdle;
+ applied_frontend_d = pending_frontend_q;
+ applied_phy_d = pending_phy_q;
+ apply_pending_d = 1'b0;
+ cfg_state_d = CfgIdle;
end
end
- default: begin
- cfg_state_d = CfgIdle;
- end
+ default: cfg_state_d = CfgInit;
endcase
end
- /////////////////////
- // State registers //
- /////////////////////
-
- `FFARN(cfg_state_q, cfg_state_d, CfgIdle, clk_i, rst_ni)
- `FFARN(cfg_applied_q, cfg_applied_d, '0, clk_i, rst_ni)
- `FFARN(clock_div_applied_q, clock_div_applied_d, '0, clk_i, rst_ni)
- `FFARN(cfg_write_pending_q, cfg_write_pending_d, 1'b0, clk_i, rst_ni)
-
- ///////////////////
- // Register bank //
- ///////////////////
+ `FFARN(cfg_state_q, cfg_state_d, CfgInit, clk_i, rst_ni)
+ `FFARN(applied_frontend_q, applied_frontend_d, '0, clk_i, rst_ni)
+ `FFARN(applied_phy_q, applied_phy_d, '0, clk_i, rst_ni)
+ `FFARN(pending_frontend_q, pending_frontend_d, '0, clk_i, rst_ni)
+ `FFARN(pending_phy_q, pending_phy_d, '0, clk_i, rst_ni)
+ `FFARN(apply_pending_q, apply_pending_d, 1'b0, clk_i, rst_ni)
hyperbus_cfg_regs #(
- .NumPhys ( NumPhys ),
- .RegDataWidth ( RegDataWidth ),
- .CapabilityFeatures ( CapabilityFeatures ),
- .reg_req_t ( reg_req_t ),
- .reg_rsp_t ( reg_rsp_t ),
- .addr_rule_t ( host_rule_t )
+ .NumPhys ( NumPhys ),
+ .RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth ),
+ .ClockDividerImplemented ( ClockDividerImplemented ),
+ .reg_req_t ( reg_req_t ),
+ .reg_rsp_t ( reg_rsp_t ),
+ .addr_rule_t ( host_rule_t )
) i_cfg_regs (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.reg_req_i ( cfg_reg_req ),
.reg_rsp_o ( cfg_reg_rsp ),
- .frontend_cfg_o ( frontend_cfg_o ),
- .phy_cfg_o ( phy_cfg ),
- .chip_rules_o ( chip_rules_o ),
- .decode_error_i ( decode_error_i )
+ .status_busy_i ( cfg_state_q != CfgIdle ),
+ .status_dirty_i ( cfg_changed ),
+ .decode_error_i ( decode_error_i ),
+ .command_flush_o( command_flush ),
+ .command_apply_o( command_apply ),
+ .frontend_cfg_o ( staged_frontend_cfg ),
+ .phy_cfg_o ( staged_phy_cfg ),
+ .chip_rules_o ( )
);
-endmodule
+ assign frontend_cfg_o = applied_frontend_q;
+ always_comb begin : proc_applied_rules
+ chip_rules_o = '0;
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ chip_rules_o[i].idx = unsigned'(i);
+ chip_rules_o[i].start_addr = applied_frontend_q.chip[i].range_base;
+ chip_rules_o[i].end_addr = applied_frontend_q.chip[i].range_bound;
+ end
+ end
+
+ for (genvar i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin : gen_cfg_range_checks
+ `ASSERT(CfgLatencyAccessRange, staged_phy_cfg.chip[i].t_latency_access >= 4'd3,
+ clk_i, !rst_ni)
+ end
+endmodule : hyperbus_cfg_frontend
diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv
index a83979b..af8a3ba 100644
--- a/src/hyperbus_cfg_regs.sv
+++ b/src/hyperbus_cfg_regs.sv
@@ -1,42 +1,40 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
-//
-// Paul Scheffler
`include "common_cells/assertions.svh"
module hyperbus_cfg_regs #(
- parameter int unsigned NumPhys = -1,
- parameter int unsigned RegDataWidth = -1,
- parameter int unsigned RegAddrWidth = 32,
- // Capability bits are supplied by the selected top-level clocking
- // implementation. The register map itself remains stable across tops.
- parameter logic [7:0] CapabilityFeatures = 8'b0010_0000,
- parameter type reg_req_t = logic,
- parameter type reg_rsp_t = logic,
- parameter type addr_rule_t = logic
+ parameter int unsigned NumPhys = -1,
+ parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
+ parameter bit ClockDividerImplemented = 1'b0,
+ parameter type reg_req_t = logic,
+ parameter type reg_rsp_t = logic,
+ parameter type addr_rule_t = logic
) (
- input logic clk_i,
- input logic rst_ni,
-
- input reg_req_t reg_req_i,
- output reg_rsp_t reg_rsp_o,
-
- output hyperbus_pkg::frontend_cfg_t frontend_cfg_o,
- output hyperbus_pkg::phy_cfg_t phy_cfg_o,
- output addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o,
- input logic decode_error_i
+ input logic clk_i,
+ input logic rst_ni,
+ input reg_req_t reg_req_i,
+ output reg_rsp_t reg_rsp_o,
+ input logic status_busy_i,
+ input logic status_dirty_i,
+ input logic decode_error_i,
+ output logic command_flush_o,
+ output logic command_apply_o,
+ output hyperbus_pkg::frontend_cfg_t frontend_cfg_o,
+ output hyperbus_pkg::phy_cfg_t phy_cfg_o,
+ output addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o
);
- `include "common_cells/registers.svh"
+ localparam int unsigned RegStrbWidth = RegDataWidth / 8;
`ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2)
- `ASSERT_INIT(RegAddrWidthValid, RegAddrWidth >= 12)
`ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32)
+ `ASSERT_INIT(RegAddrWidthValid, RegAddrWidth >= 12)
- typedef logic [11:0] cfg_addr_t;
- typedef logic [31:0] cfg_data_t;
- typedef logic [3:0] cfg_strb_t;
+ typedef logic [11:0] cfg_addr_t;
+ typedef logic [31:0] cfg_data_t;
+ typedef logic [3:0] cfg_strb_t;
typedef struct packed {
cfg_addr_t addr;
@@ -53,7 +51,7 @@ module hyperbus_cfg_regs #(
} cfg_reg_rsp_t;
typedef struct packed {
- cfg_addr_t paddr;
+ cfg_addr_t paddr;
logic [2:0] pprot;
logic psel;
logic penable;
@@ -68,161 +66,129 @@ module hyperbus_cfg_regs #(
logic pslverr;
} cfg_apb_rsp_t;
- hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t cfg_hwif_out;
hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t cfg_hwif_in;
- addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_all;
-
- cfg_addr_t cfg_addr;
- logic cfg_addr_in_window;
- logic sel_reg_mapped;
- logic cfg_access_active_d, cfg_access_active_q;
- logic cfg_access_open;
- logic cfg_status_access;
- logic cfg_value_valid;
- logic unused_cfg_fields;
-
+ hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t cfg_hwif_out;
cfg_reg_req_t cfg_reg_req;
cfg_reg_rsp_t cfg_reg_rsp;
cfg_apb_req_t cfg_apb_req;
cfg_apb_rsp_t cfg_apb_rsp;
+ cfg_addr_t cfg_addr;
+ logic addr_in_window;
+ logic cfg_addr_mapped;
+ logic cfg_value_valid;
assign cfg_addr = cfg_addr_t'(reg_req_i.addr);
- assign cfg_addr_in_window = reg_req_i.addr == RegAddrWidth'(cfg_addr);
- assign cfg_status_access = reg_req_i.valid &&
- cfg_addr_in_window && (cfg_addr == 12'h010);
+ assign addr_in_window = reg_req_i.addr == RegAddrWidth'(cfg_addr);
always_comb begin : proc_cfg_addr_mapped
- sel_reg_mapped = cfg_addr_in_window &&
- ((cfg_addr == 12'h000) ||
- (cfg_addr == 12'h004) ||
- (cfg_addr == 12'h008) ||
- (cfg_addr == 12'h00c) ||
- (cfg_addr == 12'h010) ||
- (cfg_addr == 12'h100) ||
- (cfg_addr == 12'h200) ||
- (cfg_addr == 12'hffc));
+ cfg_addr_mapped = addr_in_window &&
+ ((cfg_addr == 12'h000) ||
+ (cfg_addr == 12'h004) ||
+ (cfg_addr == 12'h008) ||
+ (cfg_addr == 12'h00c) ||
+ (cfg_addr == 12'h010) ||
+ (cfg_addr == 12'h100) ||
+ (cfg_addr == 12'h200) ||
+ (cfg_addr == 12'hffc));
for (int unsigned i = 0; i < 2; i++) begin
- sel_reg_mapped |= cfg_addr_in_window &&
- ((cfg_addr == (12'h300 + i * 12'h40)) ||
- (cfg_addr == (12'h304 + i * 12'h40)));
+ cfg_addr_mapped |= addr_in_window &&
+ ((cfg_addr == (12'h300 + i * 12'h40)) ||
+ (cfg_addr == (12'h304 + i * 12'h40)));
end
- for (int unsigned i = 0; i < 8; i++) begin
- sel_reg_mapped |= cfg_addr_in_window &&
- ((cfg_addr == (12'h400 + i * 12'h40)) ||
- (cfg_addr == (12'h404 + i * 12'h40)) ||
- (cfg_addr == (12'h408 + i * 12'h40)) ||
- (cfg_addr == (12'h40c + i * 12'h40)) ||
- (cfg_addr == (12'h410 + i * 12'h40)) ||
- (cfg_addr == (12'h414 + i * 12'h40)) ||
- (cfg_addr == (12'h418 + i * 12'h40)));
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ cfg_addr_mapped |= addr_in_window &&
+ ((cfg_addr == (12'h400 + i * 12'h40)) ||
+ (cfg_addr == (12'h404 + i * 12'h40)) ||
+ (cfg_addr == (12'h408 + i * 12'h40)) ||
+ (cfg_addr == (12'h40c + i * 12'h40)) ||
+ (cfg_addr == (12'h410 + i * 12'h40)) ||
+ (cfg_addr == (12'h414 + i * 12'h40)) ||
+ (cfg_addr == (12'h418 + i * 12'h40)));
end
end
- // The configuration frontend owns draining and automatic apply sequencing.
- // Keep this wrapper purely responsible for the stable register-map access.
- assign cfg_access_open = 1'b1;
always_comb begin : proc_cfg_value_valid
cfg_value_valid = 1'b1;
- if (reg_req_i.valid && reg_req_i.write && sel_reg_mapped) begin
- // All byte-backed legacy values are range-checked only when their
- // corresponding byte is written, preserving partial-write behavior.
+ if (reg_req_i.valid && reg_req_i.write && addr_in_window) begin
+ // Range-check byte-backed fields before narrowing them for the RTL.
if ((cfg_addr == 12'h100) && reg_req_i.wstrb[0]) begin
- cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd1);
+ cfg_value_valid &= reg_req_i.wdata[7:0] <= 8'd1;
end
if ((cfg_addr == 12'h200) && reg_req_i.wstrb[0]) begin
- cfg_value_valid &= (reg_req_i.wdata[7:0] >= 8'd2);
+ cfg_value_valid &= reg_req_i.wdata[7:0] >= 8'd2;
end
- if (((cfg_addr == 12'h304) || (cfg_addr == 12'h344)) &&
- reg_req_i.wstrb[0]) begin
- cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd15);
+ if (((cfg_addr == 12'h304) || (cfg_addr == 12'h344)) && reg_req_i.wstrb[0]) begin
+ cfg_value_valid &= reg_req_i.wdata[7:0] <= 8'd15;
end
- for (int unsigned i = 0; i < 8; i++) begin
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
if (cfg_addr == (12'h408 + i * 12'h40)) begin
- if (reg_req_i.wstrb[0]) cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd1);
- if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd31);
- if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd1);
+ if (reg_req_i.wstrb[0]) begin
+ cfg_value_valid &= reg_req_i.wdata[7:0] <= 8'd1;
+ end
+ if (reg_req_i.wstrb[1]) begin
+ cfg_value_valid &= reg_req_i.wdata[15:8] <= 8'd31;
+ end
+ if (reg_req_i.wstrb[2]) begin
+ cfg_value_valid &= reg_req_i.wdata[23:16] <= 8'd1;
+ end
end
if (cfg_addr == (12'h40c + i * 12'h40)) begin
- if (reg_req_i.wstrb[0]) cfg_value_valid &=
- (reg_req_i.wdata[7:0] >= 8'd3 && reg_req_i.wdata[7:0] <= 8'd15);
- if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd15);
- if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd1);
+ if (reg_req_i.wstrb[0]) begin
+ cfg_value_valid &= reg_req_i.wdata[7:0] inside {[8'd3:8'd15]};
+ end
+ if (reg_req_i.wstrb[1]) begin
+ cfg_value_valid &= reg_req_i.wdata[15:8] <= 8'd15;
+ end
+ if (reg_req_i.wstrb[2]) begin
+ cfg_value_valid &= reg_req_i.wdata[23:16] <= 8'd1;
+ end
end
if (cfg_addr == (12'h414 + i * 12'h40)) begin
- if (reg_req_i.wstrb[0]) cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd15);
- if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd15);
- if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd15);
+ if (reg_req_i.wstrb[0]) begin
+ cfg_value_valid &= reg_req_i.wdata[7:0] <= 8'd15;
+ end
+ if (reg_req_i.wstrb[1]) begin
+ cfg_value_valid &= reg_req_i.wdata[15:8] <= 8'd15;
+ end
+ if (reg_req_i.wstrb[2]) begin
+ cfg_value_valid &= reg_req_i.wdata[23:16] <= 8'd15;
+ end
end
end
end
end
- assign reg_rsp_o.ready = cfg_access_open &
- (~reg_req_i.valid | ~sel_reg_mapped | ~cfg_value_valid | cfg_reg_rsp.ready);
- assign reg_rsp_o.error = (reg_req_i.valid && ~sel_reg_mapped) |
- (reg_req_i.valid && reg_req_i.write && sel_reg_mapped && !cfg_value_valid) |
- cfg_reg_rsp.error;
- assign reg_rsp_o.rdata = sel_reg_mapped ? RegDataWidth'(cfg_reg_rsp.rdata) : '0;
+ assign reg_rsp_o.ready = !reg_req_i.valid || !cfg_addr_mapped ||
+ !cfg_value_valid || cfg_reg_rsp.ready;
+ assign reg_rsp_o.error = reg_req_i.valid &&
+ (!cfg_addr_mapped || !cfg_value_valid || cfg_reg_rsp.error);
+ assign reg_rsp_o.rdata = RegDataWidth'(cfg_reg_rsp.rdata);
- assign cfg_reg_req.valid = reg_req_i.valid & sel_reg_mapped & cfg_access_open & cfg_value_valid;
+ assign cfg_reg_req.valid = reg_req_i.valid && cfg_addr_mapped && cfg_value_valid;
assign cfg_reg_req.addr = cfg_addr;
assign cfg_reg_req.write = reg_req_i.write;
- assign cfg_reg_req.wdata = 32'(reg_req_i.wdata);
+ assign cfg_reg_req.wdata = cfg_data_t'(reg_req_i.wdata);
assign cfg_reg_req.wstrb = cfg_strb_t'(reg_req_i.wstrb);
- always_comb begin : proc_cfg_access
- cfg_access_active_d = cfg_access_active_q;
- if (!cfg_access_active_q && cfg_reg_req.valid && !cfg_status_access) begin
- cfg_access_active_d = 1'b1;
- end
- if (cfg_access_active_q && cfg_reg_rsp.ready) begin
- cfg_access_active_d = 1'b0;
- end
- end
-
- `FFARN(cfg_access_active_q, cfg_access_active_d, 1'b0, clk_i, rst_ni);
-
- always_comb begin : proc_chip_rules
- chip_rules_all = '0;
- for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
- chip_rules_all[i].idx = unsigned'(i);
- end
- chip_rules_all[0].start_addr = {cfg_hwif_out.chip_0.range_base.value.value, 22'b0};
- chip_rules_all[0].end_addr = {cfg_hwif_out.chip_0.range_bound.value.value, 22'b0};
- chip_rules_all[1].start_addr = {cfg_hwif_out.chip_1.range_base.value.value, 22'b0};
- chip_rules_all[1].end_addr = {cfg_hwif_out.chip_1.range_bound.value.value, 22'b0};
- chip_rules_all[2].start_addr = {cfg_hwif_out.chip_2.range_base.value.value, 22'b0};
- chip_rules_all[2].end_addr = {cfg_hwif_out.chip_2.range_bound.value.value, 22'b0};
- chip_rules_all[3].start_addr = {cfg_hwif_out.chip_3.range_base.value.value, 22'b0};
- chip_rules_all[3].end_addr = {cfg_hwif_out.chip_3.range_bound.value.value, 22'b0};
- chip_rules_all[4].start_addr = {cfg_hwif_out.chip_4.range_base.value.value, 22'b0};
- chip_rules_all[4].end_addr = {cfg_hwif_out.chip_4.range_bound.value.value, 22'b0};
- chip_rules_all[5].start_addr = {cfg_hwif_out.chip_5.range_base.value.value, 22'b0};
- chip_rules_all[5].end_addr = {cfg_hwif_out.chip_5.range_bound.value.value, 22'b0};
- chip_rules_all[6].start_addr = {cfg_hwif_out.chip_6.range_base.value.value, 22'b0};
- chip_rules_all[6].end_addr = {cfg_hwif_out.chip_6.range_bound.value.value, 22'b0};
- chip_rules_all[7].start_addr = {cfg_hwif_out.chip_7.range_base.value.value, 22'b0};
- chip_rules_all[7].end_addr = {cfg_hwif_out.chip_7.range_bound.value.value, 22'b0};
- end
-
+ // Capability and live status fields are driven by the implementation.
assign cfg_hwif_in.global_cfg.capability.num_chips.next = 8'(hyperbus_pkg::HyperNumChips);
assign cfg_hwif_in.global_cfg.capability.num_phys.next = 8'(NumPhys);
- assign cfg_hwif_in.global_cfg.capability.per_chip_cfg.next = CapabilityFeatures[0];
- assign cfg_hwif_in.global_cfg.capability.per_phy_cfg.next = CapabilityFeatures[1];
- assign cfg_hwif_in.global_cfg.capability.chip_enable.next = CapabilityFeatures[2];
- assign cfg_hwif_in.global_cfg.capability.clock_divider.next = CapabilityFeatures[3];
- assign cfg_hwif_in.global_cfg.capability.staged_apply.next = CapabilityFeatures[4];
- assign cfg_hwif_in.global_cfg.capability.error_status.next = CapabilityFeatures[5];
- assign cfg_hwif_in.global_cfg.capability.rwds_sample_timing.next =
- CapabilityFeatures[6];
- assign cfg_hwif_in.global_cfg.capability.rwds_oe_timing.next = CapabilityFeatures[7];
- assign cfg_hwif_in.global_cfg.status.decode_error.next =
+ assign cfg_hwif_in.global_cfg.capability.per_chip_cfg.next = 1'b1;
+ assign cfg_hwif_in.global_cfg.capability.per_phy_cfg.next = 1'b1;
+ assign cfg_hwif_in.global_cfg.capability.chip_enable.next = 1'b1;
+ assign cfg_hwif_in.global_cfg.capability.clock_divider.next = ClockDividerImplemented;
+ assign cfg_hwif_in.global_cfg.capability.staged_apply.next = 1'b1;
+ assign cfg_hwif_in.global_cfg.capability.error_status.next = 1'b1;
+ assign cfg_hwif_in.global_cfg.capability.rwds_sample_timing.next = 1'b0;
+ assign cfg_hwif_in.global_cfg.capability.rwds_oe_timing.next = 1'b0;
+ assign cfg_hwif_in.global_cfg.status.busy.next = status_busy_i;
+ assign cfg_hwif_in.global_cfg.status.dirty.next = status_dirty_i;
+ assign cfg_hwif_in.global_cfg.status.decode_error.next =
cfg_hwif_out.global_cfg.status.decode_error.value;
assign cfg_hwif_in.global_cfg.status.decode_error.hwset = decode_error_i;
- assign cfg_hwif_in.global_cfg.status.busy.next = 1'b0;
- assign cfg_hwif_in.global_cfg.status.dirty.next = 1'b0;
- // COMMAND fields are intentionally left unconsumed until staged apply is
- // implemented. STATUS busy/dirty likewise have no point-3 source.
+
+ assign command_flush_o = cfg_hwif_out.global_cfg.command.flush.value;
+ assign command_apply_o = cfg_hwif_out.global_cfg.command.apply.value;
reg_to_apb #(
.reg_req_t ( cfg_reg_req_t ),
@@ -239,67 +205,168 @@ module hyperbus_cfg_regs #(
);
hyperbus_cfg_regblock i_cfg_regblock (
- .clk ( clk_i ),
- .arst_n ( rst_ni ),
- .s_apb_psel ( cfg_apb_req.psel ),
- .s_apb_penable ( cfg_apb_req.penable ),
- .s_apb_pwrite ( cfg_apb_req.pwrite ),
- .s_apb_pprot ( cfg_apb_req.pprot ),
- .s_apb_paddr ( cfg_apb_req.paddr ),
- .s_apb_pwdata ( cfg_apb_req.pwdata ),
- .s_apb_pstrb ( cfg_apb_req.pstrb ),
- .s_apb_pready ( cfg_apb_rsp.pready ),
- .s_apb_prdata ( cfg_apb_rsp.prdata ),
- .s_apb_pslverr ( cfg_apb_rsp.pslverr ),
- .hwif_in ( cfg_hwif_in ),
- .hwif_out ( cfg_hwif_out )
+ .clk ( clk_i ),
+ .arst_n ( rst_ni ),
+ .s_apb_psel ( cfg_apb_req.psel ),
+ .s_apb_penable ( cfg_apb_req.penable ),
+ .s_apb_pwrite ( cfg_apb_req.pwrite ),
+ .s_apb_pprot ( cfg_apb_req.pprot ),
+ .s_apb_paddr ( cfg_apb_req.paddr ),
+ .s_apb_pwdata ( cfg_apb_req.pwdata ),
+ .s_apb_pstrb ( cfg_apb_req.pstrb ),
+ .s_apb_pready ( cfg_apb_rsp.pready ),
+ .s_apb_prdata ( cfg_apb_rsp.prdata ),
+ .s_apb_pslverr ( cfg_apb_rsp.pslverr ),
+ .hwif_in ( cfg_hwif_in ),
+ .hwif_out ( cfg_hwif_out )
);
always_comb begin : proc_cfg_output
frontend_cfg_o = '0;
phy_cfg_o = '0;
- frontend_cfg_o.address_mask_msb = cfg_hwif_out.chip_0.address_cfg.address_mask_msb.value;
- frontend_cfg_o.address_space = cfg_hwif_out.chip_0.address_cfg.address_space.value[0];
- frontend_cfg_o.dual_phy = (NumPhys == 2) &&
+ frontend_cfg_o.dual_phy = (NumPhys == 2) &&
cfg_hwif_out.frontend.frontend_cfg.dual_phy.value;
- frontend_cfg_o.phy_clock_div = cfg_hwif_out.backend.clock_cfg.divider.value;
+ frontend_cfg_o.divider = cfg_hwif_out.backend.clock_cfg.divider.value;
- phy_cfg_o.chip.t_latency_access = cfg_hwif_out.chip_0.latency_cfg.t_latency_access.value;
- phy_cfg_o.chip.en_latency_additional = cfg_hwif_out.chip_0.latency_cfg.en_latency_additional.value;
- phy_cfg_o.chip.t_burst_max = cfg_hwif_out.chip_0.burst_cfg.t_burst_max.value;
- phy_cfg_o.chip.t_read_write_recovery = cfg_hwif_out.chip_0.chip_timing.t_read_write_recovery.value;
- phy_cfg_o.chip.t_rx_clk_delay = cfg_hwif_out.chip_0.rx_delay.value.value;
- phy_cfg_o.chip.t_csh_cycles = cfg_hwif_out.chip_0.chip_timing.t_csh_cycles.value;
- phy_cfg_o.chip.csn_to_ck_cycles = cfg_hwif_out.chip_0.chip_timing.csn_to_ck_cycles.value;
- phy_cfg_o.t_tx_clk_delay = cfg_hwif_out.phy_0.tx_delay.value.value;
- phy_cfg_o.dual_phy = (NumPhys == 2) &&
- cfg_hwif_out.frontend.frontend_cfg.dual_phy.value;
- end
+ phy_cfg_o.dual_phy = frontend_cfg_o.dual_phy;
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ unique case (i)
+ 0: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_0.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_0.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_0.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_0.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_0.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_0.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_0.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_0.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_0.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_0.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_0.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_0.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_0.rx_delay.value.value;
+ end
+ 1: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_1.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_1.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_1.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_1.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_1.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_1.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_1.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_1.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_1.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_1.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_1.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_1.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_1.rx_delay.value.value;
+ end
+ 2: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_2.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_2.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_2.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_2.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_2.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_2.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_2.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_2.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_2.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_2.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_2.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_2.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_2.rx_delay.value.value;
+ end
+ 3: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_3.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_3.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_3.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_3.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_3.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_3.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_3.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_3.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_3.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_3.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_3.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_3.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_3.rx_delay.value.value;
+ end
+ 4: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_4.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_4.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_4.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_4.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_4.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_4.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_4.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_4.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_4.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_4.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_4.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_4.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_4.rx_delay.value.value;
+ end
+ 5: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_5.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_5.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_5.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_5.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_5.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_5.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_5.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_5.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_5.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_5.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_5.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_5.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_5.rx_delay.value.value;
+ end
+ 6: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_6.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_6.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_6.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_6.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_6.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_6.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_6.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_6.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_6.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_6.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_6.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_6.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_6.rx_delay.value.value;
+ end
+ default: begin
+ frontend_cfg_o.chip[i].range_base = {cfg_hwif_out.chip_7.range_base.value.value, 22'b0};
+ frontend_cfg_o.chip[i].range_bound = {cfg_hwif_out.chip_7.range_bound.value.value, 22'b0};
+ frontend_cfg_o.chip[i].address_space = cfg_hwif_out.chip_7.address_cfg.address_space.value[0];
+ frontend_cfg_o.chip[i].address_mask_msb = cfg_hwif_out.chip_7.address_cfg.address_mask_msb.value[4:0];
+ frontend_cfg_o.chip[i].enable = cfg_hwif_out.chip_7.address_cfg.enable.value;
+ phy_cfg_o.chip[i].t_latency_access = cfg_hwif_out.chip_7.latency_cfg.t_latency_access.value[3:0];
+ phy_cfg_o.chip[i].rwds_sample_delay = cfg_hwif_out.chip_7.latency_cfg.rwds_sample_delay.value[3:0];
+ phy_cfg_o.chip[i].en_latency_additional = cfg_hwif_out.chip_7.latency_cfg.en_latency_additional.value;
+ phy_cfg_o.chip[i].t_burst_max = cfg_hwif_out.chip_7.burst_cfg.t_burst_max.value;
+ phy_cfg_o.chip[i].t_read_write_recovery = cfg_hwif_out.chip_7.chip_timing.t_read_write_recovery.value[3:0];
+ phy_cfg_o.chip[i].t_csh_cycles = cfg_hwif_out.chip_7.chip_timing.t_csh_cycles.value[3:0];
+ phy_cfg_o.chip[i].csn_to_ck_cycles = cfg_hwif_out.chip_7.chip_timing.csn_to_ck_cycles.value[3:0];
+ phy_cfg_o.chip[i].t_rx_clk_delay = cfg_hwif_out.chip_7.rx_delay.value.value;
+ end
+ endcase
+ end
- // The legacy PR34 interface has no destinations for these newer fields.
- always_comb begin : proc_unused_cfg_fields
- unused_cfg_fields = ^cfg_hwif_out.backend.clock_cfg.divider.value;
- unused_cfg_fields |= ^cfg_hwif_out.phy_0.rwds_timing.rwds_oe_setup_cycles.value;
- unused_cfg_fields |= ^cfg_hwif_out.phy_1.rwds_timing.rwds_oe_setup_cycles.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_0.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_0.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_1.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_1.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_2.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_2.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_3.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_3.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_4.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_4.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_5.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_5.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_6.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_6.latency_cfg.rwds_sample_delay.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_7.address_cfg.enable.value;
- unused_cfg_fields |= ^cfg_hwif_out.chip_7.latency_cfg.rwds_sample_delay.value;
+ phy_cfg_o.phy[0].tx_delay = cfg_hwif_out.phy_0.tx_delay.value.value;
+ phy_cfg_o.phy[0].rwds_oe_setup_cycles = cfg_hwif_out.phy_0.rwds_timing.rwds_oe_setup_cycles.value[3:0];
+ phy_cfg_o.phy[1].tx_delay = cfg_hwif_out.phy_1.tx_delay.value.value;
+ phy_cfg_o.phy[1].rwds_oe_setup_cycles = cfg_hwif_out.phy_1.rwds_timing.rwds_oe_setup_cycles.value[3:0];
end
- assign chip_rules_o = chip_rules_all;
-
+ always_comb begin : proc_chip_rules
+ chip_rules_o = '0;
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ chip_rules_o[i].idx = unsigned'(i);
+ chip_rules_o[i].start_addr = frontend_cfg_o.chip[i].range_base;
+ chip_rules_o[i].end_addr = frontend_cfg_o.chip[i].range_bound;
+ end
+ end
endmodule : hyperbus_cfg_regs
diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv
index 74b4d62..468b0bd 100644
--- a/src/hyperbus_isochronous.sv
+++ b/src/hyperbus_isochronous.sv
@@ -15,6 +15,7 @@ module hyperbus_isochronous #(
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic,
parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
parameter type reg_req_t = logic,
parameter type reg_rsp_t = logic,
parameter type axi_rule_t = logic,
@@ -176,10 +177,9 @@ module hyperbus_isochronous #(
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.host_rule_t ( axi_rule_t ),
- .RegDataWidth ( RegDataWidth ),
- // Isochronous top implements the backend clock divider; decode-error
- // status is shared by all top-level clocking variants.
- .CapabilityFeatures ( 8'b0010_1000 )
+ .RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth ),
+ .ClockDividerImplemented ( 1'b1 )
) i_cfg_frontend (
.clk_i ( clk_sys_i ),
.rst_ni ( rst_sys_ni ),
diff --git a/src/hyperbus_midend.sv b/src/hyperbus_midend.sv
index 8b25cf2..2b06bbb 100644
--- a/src/hyperbus_midend.sv
+++ b/src/hyperbus_midend.sv
@@ -216,6 +216,7 @@ module hyperbus_midend #(
host_ext_addr_t req_last_addr;
host_ext_addr_t req_phy_bytes;
host_ext_addr_t cmd_rule_end_addr;
+ host_ext_addr_t covered_rule_end_addr;
hyperbus_pkg::hyper_blen_t req_phy_burst;
logic cmd_dec_valid;
logic cmd_end_dec_valid;
@@ -354,37 +355,30 @@ module hyperbus_midend #(
(req_phy_end_addr > (host_ext_addr_t'(1) << HostAddrWidth));
end
- addr_decode #(
- .NoIndices ( hyperbus_pkg::HyperNumChips ),
- .NoRules ( hyperbus_pkg::HyperNumChips ),
- .addr_t ( host_addr_t ),
- .rule_t ( rule_t ),
- .idx_t ( chip_sel_idx_t )
- ) i_start_addr_decode (
- .addr_i ( req_phy_first_addr ),
- .addr_map_i ( chip_rules_i ),
- .idx_o ( cmd_chip_sel_idx ),
- .dec_valid_o ( cmd_dec_valid ),
- .dec_error_o ( ),
- .en_default_idx_i ( 1'b0 ),
- .default_idx_i ( '0 )
- );
-
- addr_decode #(
- .NoIndices ( hyperbus_pkg::HyperNumChips ),
- .NoRules ( hyperbus_pkg::HyperNumChips ),
- .addr_t ( host_addr_t ),
- .rule_t ( rule_t ),
- .idx_t ( chip_sel_idx_t )
- ) i_end_addr_decode (
- .addr_i ( req_phy_last_addr ),
- .addr_map_i ( chip_rules_i ),
- .idx_o ( cmd_end_chip_sel_idx ),
- .dec_valid_o ( cmd_end_dec_valid ),
- .dec_error_o ( ),
- .en_default_idx_i ( 1'b0 ),
- .default_idx_i ( '0 )
- );
+ // Decode only enabled rules; common_cells addr_decode has no per-rule enable.
+ always_comb begin : proc_chip_decode
+ cmd_chip_sel_idx = '0;
+ cmd_end_chip_sel_idx = '0;
+ cmd_dec_valid = 1'b0;
+ cmd_end_dec_valid = 1'b0;
+
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ if (frontend_cfg_i.chip[i].enable &&
+ (req_phy_first_addr >= chip_rules_i[i].start_addr) &&
+ ((req_phy_first_addr < chip_rules_i[i].end_addr) ||
+ (chip_rules_i[i].end_addr == '0))) begin
+ cmd_chip_sel_idx = chip_sel_idx_t'(i);
+ cmd_dec_valid = 1'b1;
+ end
+ if (frontend_cfg_i.chip[i].enable &&
+ (req_phy_last_addr >= chip_rules_i[i].start_addr) &&
+ ((req_phy_last_addr < chip_rules_i[i].end_addr) ||
+ (chip_rules_i[i].end_addr == '0))) begin
+ cmd_end_chip_sel_idx = chip_sel_idx_t'(i);
+ cmd_end_dec_valid = 1'b1;
+ end
+ end
+ end
// Software keeps ranges ordered and non-overlapping; transactions may cross contiguous ranges.
always_comb begin : proc_req_rule_range
@@ -395,13 +389,22 @@ module hyperbus_midend #(
host_ext_addr_t'(
chip_rules_i[cmd_chip_sel_idx].end_addr);
end
+ covered_rule_end_addr = cmd_rule_end_addr;
req_range_valid = !req_addr_overflow && cmd_dec_valid && cmd_end_dec_valid &&
- (cmd_end_chip_sel_idx >= cmd_chip_sel_idx);
- for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips - 1; i++) begin
- if ((i >= cmd_chip_sel_idx) && (i < cmd_end_chip_sel_idx) &&
- (chip_rules_i[i].end_addr != chip_rules_i[i+1].start_addr)) begin
- req_range_valid = 1'b0;
+ (cmd_end_chip_sel_idx >= cmd_chip_sel_idx) &&
+ frontend_cfg_i.chip[cmd_chip_sel_idx].enable &&
+ frontend_cfg_i.chip[cmd_end_chip_sel_idx].enable;
+ for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin
+ if ((i > cmd_chip_sel_idx) && (i <= cmd_end_chip_sel_idx) &&
+ frontend_cfg_i.chip[i].enable) begin
+ if (host_ext_addr_t'(chip_rules_i[i].start_addr) !=
+ covered_rule_end_addr) begin
+ req_range_valid = 1'b0;
+ end
+ covered_rule_end_addr = (chip_rules_i[i].end_addr == '0) ?
+ (host_ext_addr_t'(1) << HostAddrWidth) :
+ host_ext_addr_t'(chip_rules_i[i].end_addr);
end
end
end
@@ -415,13 +418,15 @@ module hyperbus_midend #(
assign cmd_o.trans.write = cmd_req.write;
assign cmd_o.trans.burst_type = 1'b1; // Wrapping HyperBus bursts are not supported.
- assign cmd_o.trans.address_space = frontend_cfg_i.address_space;
+ assign cmd_o.trans.address_space = cmd_dec_valid ?
+ frontend_cfg_i.chip[cmd_chip_sel_idx].address_space : 1'b0;
host_addr_t cmd_phy_address;
host_addr_t masked_req_address;
always_comb begin : proc_cmd_address
masked_req_address = req_phy_first_addr &
- ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1);
+ ((host_addr_t'(1) << (cmd_dec_valid ?
+ frontend_cfg_i.chip[cmd_chip_sel_idx].address_mask_msb : 5'd0)) - 1);
cmd_phy_address = masked_req_address >> 1;
if (NumPhys == 2) begin
cmd_phy_address = masked_req_address >> 2;
diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv
index 589a57a..1c22ab4 100644
--- a/src/hyperbus_pkg.sv
+++ b/src/hyperbus_pkg.sv
@@ -50,26 +50,39 @@ package hyperbus_pkg;
///////////////////
typedef struct packed {
- logic [3:0] t_latency_access;
- logic en_latency_additional;
- logic [15:0] t_burst_max;
- logic [3:0] t_read_write_recovery;
- logic [7:0] t_rx_clk_delay;
- logic [3:0] t_csh_cycles; // CS-high recovery cycles
- logic [3:0] csn_to_ck_cycles; // CS assertion to clock-start delay
+ logic [31:0] range_base;
+ logic [31:0] range_bound;
+ logic [4:0] address_mask_msb;
+ logic address_space;
+ logic enable;
+ } chip_frontend_cfg_t;
+
+ typedef struct packed {
+ logic [3:0] t_latency_access;
+ logic en_latency_additional;
+ logic [3:0] rwds_sample_delay;
+ logic [15:0] t_burst_max;
+ logic [3:0] t_read_write_recovery;
+ logic [7:0] t_rx_clk_delay;
+ logic [3:0] t_csh_cycles;
+ logic [3:0] csn_to_ck_cycles;
} chip_phy_cfg_t;
typedef struct packed {
- logic [4:0] address_mask_msb;
- logic address_space;
- logic dual_phy;
- logic [7:0] phy_clock_div;
+ logic [7:0] tx_delay;
+ logic [3:0] rwds_oe_setup_cycles;
+ } phy_lane_cfg_t;
+
+ typedef struct packed {
+ chip_frontend_cfg_t [HyperNumChips-1:0] chip;
+ logic dual_phy;
+ logic [7:0] divider;
} frontend_cfg_t;
typedef struct packed {
- chip_phy_cfg_t chip;
- logic [7:0] t_tx_clk_delay;
- logic dual_phy;
+ chip_phy_cfg_t [HyperNumChips-1:0] chip;
+ phy_lane_cfg_t [1:0] phy;
+ logic dual_phy;
} phy_cfg_t;
//////////////////////////
diff --git a/src/hyperbus_synchronous.sv b/src/hyperbus_synchronous.sv
index fdca6cd..1f1e1ee 100644
--- a/src/hyperbus_synchronous.sv
+++ b/src/hyperbus_synchronous.sv
@@ -14,6 +14,7 @@ module hyperbus_synchronous #(
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic,
parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
parameter type reg_req_t = logic,
parameter type reg_rsp_t = logic,
parameter type axi_rule_t = logic,
@@ -117,7 +118,8 @@ module hyperbus_synchronous #(
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.host_rule_t ( axi_rule_t ),
- .RegDataWidth ( RegDataWidth )
+ .RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth )
) i_cfg_frontend (
.clk_i ( clk_sys_i ),
.rst_ni ( rst_sys_ni ),
diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl
index a81f7f9..6be19e4 100644
--- a/src/regs/hyperbus_cfg_regs.rdl
+++ b/src/regs/hyperbus_cfg_regs.rdl
@@ -68,7 +68,7 @@ addrmap hyperbus_cfg_regs {
field { sw = rw; hw = r; reset = 1'b1; } enable[16:16];
};
reg latency_cfg {
- desc = "Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.";
+ desc = "Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most t_latency_access minus 2 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.";
field { sw = rw; hw = r; reset = 8'd6; } t_latency_access[7:0];
field { sw = rw; hw = r; reset = 8'd0; } rwds_sample_delay[15:8];
field { sw = rw; hw = r; reset = 1'b0; } en_latency_additional[16:16];
diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv
index 263eb71..24fa961 100644
--- a/test/axi_hyper_tb.sv
+++ b/test/axi_hyper_tb.sv
@@ -72,7 +72,6 @@ module axi_hyper_tb
typedef axi_pkg::xbar_rule_32_t rule_t;
localparam int unsigned RegBusDW = 32;
- // The stable register map is a hierarchical 4 KiB window.
localparam int unsigned RegBusAW = 12;
localparam int unsigned TbDramDataWidth = 8;
@@ -578,6 +577,26 @@ module axi_hyper_tb
end
endtask
+ task automatic wait_config_barrier(input reg_bus_master_t reg_drv);
+ logic [31:0] status;
+ logic reg_error;
+ // Allow the generated command pulse to reach the configuration FSM.
+ repeat (2) @(posedge clk);
+ for (int unsigned i = 0; i < 10000; i++) begin
+ reg_drv.send_read(32'h010, status, reg_error);
+ if (reg_error != 1'b0) $error("configuration STATUS read failed");
+ if (!status[1]) return;
+ end
+ $error("configuration barrier did not return idle");
+ endtask
+
+ task automatic apply_config(input reg_bus_master_t reg_drv);
+ logic reg_error;
+ reg_drv.send_write(32'h00c, 32'h2, '1, reg_error);
+ if (reg_error != 1'b0) $error("configuration APPLY write failed");
+ wait_config_barrier(reg_drv);
+ endtask
+
task automatic run_performance_smoke(input axi_ctrl_master_t axi_drv);
localparam int unsigned NumCases = 5;
localparam axi_addr_t PerfBaseAddr = axi_addr_t'(32'h8000_8000);
@@ -706,6 +725,7 @@ module axi_hyper_tb
reg_drv.send_write(32'h410, TbSlowBurstMax, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
segment_start_snapshot = segment_start_count;
axi_write_slow(axi_drv, SlowBaseAddr, TbSlowNumBeats, TbSlowGapCycles);
@@ -731,6 +751,7 @@ module axi_hyper_tb
reg_drv.send_write(32'h410, saved_t_burst_max, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
endtask
task automatic check_config_barrier(
@@ -740,12 +761,12 @@ module axi_hyper_tb
localparam axi_addr_t BarrierAddr = axi_addr_t'(32'h8000_6000);
logic reg_error;
time write_done_time;
- time cfg_done_time;
+ time flush_done_time;
write_done_time = 0;
- cfg_done_time = 0;
+ flush_done_time = 0;
$display("===========================");
- $display("= Config drain/apply =");
+ $display("= Config flush barrier =");
$display("===========================");
fork
@@ -755,19 +776,18 @@ module axi_hyper_tb
end
begin
repeat (32) @(posedge clk);
- // Any accepted configuration write triggers the automatic drain/apply
- // sequence; COMMAND remains inert until staged apply is implemented.
- reg_drv.send_write(32'h410, 32'd350, '1, reg_error);
- cfg_done_time = $time;
+ reg_drv.send_write(32'h00c, 32'h1, '1, reg_error);
+ wait_config_barrier(reg_drv);
+ flush_done_time = $time;
if (reg_error != 1'b0) begin
- $error("[CFG-DRAIN] Configuration write returned an error");
+ $error("[CFG-BARRIER] Flush register write returned an error");
end
end
join
- if ((write_done_time == 0) || (cfg_done_time < write_done_time)) begin
- $error("[CFG-DRAIN] Configuration completed at %0t before AXI write completed at %0t",
- cfg_done_time, write_done_time);
+ if ((write_done_time == 0) || (flush_done_time < write_done_time)) begin
+ $error("[CFG-BARRIER] Flush completed at %0t before AXI write completed at %0t",
+ flush_done_time, write_done_time);
end
endtask
@@ -813,13 +833,13 @@ module axi_hyper_tb
$error("[DECODE] Invalid write returned response %0d", b.b_resp);
end
- reg_drv.send_read(32'h010, status, reg_error);
+ reg_drv.send_read(32'h10, status, reg_error);
if ((reg_error != 1'b0) || !status[0]) begin
$error("[DECODE] Sticky decode-error status was not set");
end
- reg_drv.send_write(32'h010, 32'h1, '1, reg_error);
+ reg_drv.send_write(32'h10, 32'h1, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
- reg_drv.send_read(32'h010, status, reg_error);
+ reg_drv.send_read(32'h10, status, reg_error);
if ((reg_error != 1'b0) || status[0]) begin
$error("[DECODE] Sticky decode-error status did not clear");
end
@@ -846,6 +866,7 @@ module axi_hyper_tb
if (reg_error != 1'b0) $error("unexpected error");
reg_drv.send_write(32'h440, Boundary, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
segment_start_snapshot = segment_start_count;
axi_write_slow(axi_drv, BurstAddr, 4, 0);
@@ -859,6 +880,34 @@ module axi_hyper_tb
if (reg_error != 1'b0) $error("unexpected error");
reg_drv.send_write(32'h404, 32'h8100_0000, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
+ endtask
+
+ task automatic check_chip_enable(
+ input axi_ctrl_master_t axi_drv,
+ input reg_bus_master_t reg_drv
+ );
+ axi_ctrl_master_t::ax_beat_t ax = new();
+ axi_ctrl_master_t::r_beat_t r;
+ logic reg_error;
+ if (NumConnectedChips < 2) return;
+ $display("===========================");
+ $display("= Per-chip enable =");
+ $display("===========================");
+ reg_drv.send_write(32'h448, 32'h1900, '1, reg_error);
+ if (reg_error != 1'b0) $error("chip disable write failed");
+ apply_config(reg_drv);
+ ax.ax_addr = 32'h8100_0000;
+ ax.ax_id = '0;
+ ax.ax_len = 0;
+ ax.ax_size = 3;
+ ax.ax_burst = axi_pkg::BURST_INCR;
+ axi_drv.send_ar(ax);
+ axi_drv.recv_r(r);
+ if (r.r_resp != axi_pkg::RESP_DECERR) $error("disabled chip accepted a request");
+ reg_drv.send_write(32'h448, 32'h11900, '1, reg_error);
+ if (reg_error != 1'b0) $error("chip enable write failed");
+ apply_config(reg_drv);
endtask
task automatic check_large_rule_distance(input axi_ctrl_master_t axi_drv);
@@ -904,6 +953,7 @@ module axi_hyper_tb
// A zero bound extends the final rule through the end of the address space.
reg_drv.send_write(32'h444, '0, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
axi_write_subword(axi_drv, ValidAddr, TestData, 3);
axi_check_subword(axi_drv, ValidAddr, TestData, 3);
@@ -925,6 +975,7 @@ module axi_hyper_tb
reg_drv.send_write(32'h444, 32'h8200_0000, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
endtask
task automatic check_atomic_add(
@@ -986,11 +1037,11 @@ module axi_hyper_tb
$error("[ATOMIC] Unsupported operation returned rresp=%0d bresp=%0d",
r.r_resp, b.b_resp);
end
- reg_drv.send_read(32'h010, status, reg_error);
+ reg_drv.send_read(32'h10, status, reg_error);
if ((reg_error != 1'b0) || !status[0]) begin
$error("[ATOMIC] Unsupported operation did not set sticky error status");
end
- reg_drv.send_write(32'h010, 32'h1, '1, reg_error);
+ reg_drv.send_write(32'h10, 32'h1, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
axi_check_subword(axi_drv, AtomicAddr, InitialValue + Addend, 2);
@@ -1014,11 +1065,11 @@ module axi_hyper_tb
$error("[ATOMIC] Multi-beat operation returned rresp=%0d bresp=%0d",
r.r_resp, b.b_resp);
end
- reg_drv.send_read(32'h010, status, reg_error);
+ reg_drv.send_read(32'h10, status, reg_error);
if ((reg_error != 1'b0) || !status[0]) begin
$error("[ATOMIC] Malformed operation did not set sticky error status");
end
- reg_drv.send_write(32'h010, 32'h1, '1, reg_error);
+ reg_drv.send_write(32'h10, 32'h1, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
axi_write_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2);
axi_check_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2);
@@ -1079,6 +1130,7 @@ module axi_hyper_tb
// A zero-ended final rule must contain ordinary atomic accesses.
reg_drv.send_write(32'h444, '0, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
axi_write_subword(axi_drv, ZeroEndAddr, ZeroEndInitial, 2);
ax.ax_addr = ZeroEndAddr;
ax.ax_size = 2;
@@ -1098,6 +1150,7 @@ module axi_hyper_tb
axi_check_subword(axi_drv, ZeroEndAddr, ZeroEndInitial + ZeroEndAddend, 2);
reg_drv.send_write(32'h444, 32'h8200_0000, '1, reg_error);
if (reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_drv);
endtask
initial begin : proc_sim_crtl
@@ -1144,12 +1197,21 @@ module axi_hyper_tb
if (s_reg_error != 1'b0) $error("unexpected error");
reg_master.send_write(32'h300, TbTxDelayLineTaps, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ if (NumPhys == 2) begin
+ reg_master.send_write(32'h340, TbTxDelayLineTaps + 1, '1, s_reg_error);
+ end
+ reg_master.send_read(32'h010, reg_read, s_reg_error);
+ if (s_reg_error != 1'b0 || !reg_read[2]) $error("staged configuration did not set STATUS.dirty");
+ apply_config(reg_master);
+ reg_master.send_read(32'h010, reg_read, s_reg_error);
+ if (s_reg_error != 1'b0 || reg_read[2] || reg_read[1]) $error("APPLY did not clear STATUS.dirty/busy");
if (TbDutVariant == 0) begin
reg_master.send_read(32'h200, reg_read, s_reg_error);
if ((s_reg_error != 1'b0) || (reg_read != 8)) $error("unexpected divider reset value");
reg_master.send_write(32'h200, 8'd2, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_master);
end
#600350ns;
@@ -1158,6 +1220,7 @@ module axi_hyper_tb
// The configuration barrier completes only after the divided clock resumes.
reg_master.send_write(32'h200, 8'd4, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_master);
reg_master.send_read(32'h200, reg_read, s_reg_error);
if ((s_reg_error != 1'b0) || (reg_read != 4)) $error("divider update failed");
@@ -1167,6 +1230,7 @@ module axi_hyper_tb
reg_master.send_write(32'h200, 8'd2, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_master);
reg_master.send_read(32'h200, reg_read, s_reg_error);
if ((s_reg_error != 1'b0) || (reg_read != 2)) $error("divider restore failed");
@@ -1185,6 +1249,7 @@ module axi_hyper_tb
run_slow_backpressure_test(axi_ctrl_mst, reg_master);
check_config_barrier(axi_ctrl_mst, reg_master);
check_decode_errors(axi_ctrl_mst, reg_master);
+ check_chip_enable(axi_ctrl_mst, reg_master);
check_cross_chip_burst(axi_ctrl_mst, reg_master);
check_large_rule_distance(axi_ctrl_mst);
check_range_edges(axi_ctrl_mst, reg_master);
@@ -1201,16 +1266,20 @@ module axi_hyper_tb
// Keep each data phase near 2 us, leaving margin for CA and access latency.
reg_master.send_write(32'h410, 400 / iso_test_dividers[i], '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected t_burst_max update error");
+ apply_config(reg_master);
reg_master.send_write(32'h200, iso_test_dividers[i], '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected divider update error");
+ apply_config(reg_master);
axi_write_slow(axi_ctrl_mst, 32'h8001_0000 + i * 32'h100, 16, 0);
axi_read_slow_check(axi_ctrl_mst, 32'h8001_0000 + i * 32'h100, 16, 128);
end
reg_master.send_write(32'h200, 8'd2, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected divider restore error");
+ apply_config(reg_master);
reg_master.send_write(32'h410, iso_saved_t_burst_max, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected t_burst_max restore error");
+ apply_config(reg_master);
end
if (NumPhys == 1) begin
@@ -1219,16 +1288,18 @@ module axi_hyper_tb
if (TbDutVariant == 0) begin
// switch memory address space to register space
- reg_master.send_write(32'h408, 32'h1_1901, '1, s_reg_error);
+ reg_master.send_write(32'h408, 32'h11901, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_master);
// enable variable latency so we can test RWDS sampling
s27ks_cfg0.fixed_latency_enable = 1'b0;
axi_write_32(32'h8000_0000 + S27KS_CFG0_REG_OFFSET, (s27ks_cfg0 | s27ks_cfg0 << 16));
// switch back to memory address space
- reg_master.send_write(32'h408, 32'h1_1900, '1, s_reg_error);
+ reg_master.send_write(32'h408, 32'h11900, '1, s_reg_error);
if (s_reg_error != 1'b0) $error("unexpected error");
+ apply_config(reg_master);
end
check_consecutive_reads(axi_ctrl_mst);
@@ -1254,6 +1325,7 @@ module axi_hyper_tb
$display("===========================");
reg_master.send_write(32'h100,1'b0,'1,s_reg_error);
+ apply_config(reg_master);
if (s_reg_error != 1'b0) $error("unexpected error");
axi_rand_mst.reset();
diff --git a/test/axi_pre_aw_drain_tb.sv b/test/axi_pre_aw_drain_tb.sv
index 74f3f88..e67db13 100644
--- a/test/axi_pre_aw_drain_tb.sv
+++ b/test/axi_pre_aw_drain_tb.sv
@@ -13,17 +13,11 @@ module axi_pre_aw_drain_tb;
localparam int unsigned AxiDataBytes = 8;
localparam int unsigned NumTransactions = HostWriteBufferBytes / AxiDataBytes;
localparam int unsigned PartialBurstBeats = 32;
- localparam logic [31:0] BaseAddress = 32'h0000_6800;
- localparam logic [31:0] PartialBaseAddress = 32'h0000_7000;
- // A regular stable-map configuration write starts the automatic drain/apply
- // barrier in point5. The staged COMMAND/STATUS interface is not enabled.
- localparam logic [31:0] DrainConfigAddr = 32'h410;
- localparam logic [31:0] DrainConfigValue = 32'd350;
-
- bit complete_config_done;
+ localparam logic [31:0] BaseAddress = 32'h8000_6800;
+ localparam logic [31:0] PartialBaseAddress = 32'h8000_7000;
+
bit partial_w_fifo_full;
- bit partial_config_busy;
- bit partial_config_done;
+ bit partial_flush_busy;
bit partial_w_done;
fixture_hyperbus #(
@@ -35,8 +29,49 @@ module axi_pre_aw_drain_tb;
.AnnotateSdf ( 1'b1 )
) fix ();
+ task automatic apply_default_mapping();
+ logic [31:0] status;
+ logic error;
+
+ fix.i_rmaster.send_write(32'h444, 32'h8200_0000, '1, error);
+ if (error) $fatal(1, "chip 1 start-address write failed");
+ fix.i_rmaster.send_write(32'h440, 32'h8100_0000, '1, error);
+ if (error) $fatal(1, "chip 1 end-address write failed");
+ fix.i_rmaster.send_write(32'h404, 32'h8100_0000, '1, error);
+ if (error) $fatal(1, "chip 0 end-address write failed");
+ fix.i_rmaster.send_write(32'h400, 32'h8000_0000, '1, error);
+ if (error) $fatal(1, "chip 0 start-address write failed");
+ fix.i_rmaster.send_write(32'h00c, 32'h2, '1, error);
+ if (error) $fatal(1, "configuration APPLY write failed");
+
+ repeat (4) @(posedge fix.sys_clk);
+ for (int unsigned poll = 0; poll < 1000; poll++) begin
+ fix.i_rmaster.send_read(32'h010, status, error);
+ if (error) $fatal(1, "configuration status read failed");
+ if (!status[1]) return;
+ end
+ $fatal(1, "configuration APPLY did not become idle");
+ endtask
+
+ task automatic wait_barrier_idle();
+ logic [31:0] status;
+ logic error;
+
+ repeat (2) @(posedge fix.sys_clk);
+ for (int unsigned poll = 0; poll < 1000; poll++) begin
+ fix.i_rmaster.send_read(32'h010, status, error);
+ if (error) $fatal(1, "configuration status read failed");
+ if (!status[1]) return;
+ end
+ $fatal(1, "configuration barrier did not become idle");
+ endtask
+
initial begin
+ logic [31:0] status;
+ logic error;
+
fix.reset_end();
+ apply_default_mapping();
// Fill all 16 entries of the 128-byte host W FIFO without presenting AW.
for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin
@@ -47,20 +82,15 @@ module axi_pre_aw_drain_tb;
fix.axi_master_drv.send_w(fix.w_beat);
end
- // Start the automatic configuration barrier, then verify it remains busy until the
+ // Start the existing flush barrier, then verify it remains busy until the
// matching AW requests have made the buffered W beats actionable.
- complete_config_done = 1'b0;
- fork
- begin : config_complete_w_bursts
- logic config_error;
- fix.i_rmaster.send_write(DrainConfigAddr, DrainConfigValue, '1, config_error);
- if (config_error) $fatal(1, "drain configuration write failed");
- complete_config_done = 1'b1;
- end
- join_none
- repeat (4) @(posedge fix.sys_clk);
- if (complete_config_done) begin
- $fatal(1, "drain configuration write completed before buffered W bursts drained");
+ fix.i_rmaster.send_write(32'h00c, 32'h1, '1, error);
+ if (error) $fatal(1, "configuration FLUSH write failed");
+ for (int unsigned poll = 0; poll < 100; poll++) begin
+ fix.i_rmaster.send_read(32'h010, status, error);
+ if (error) $fatal(1, "configuration status read failed");
+ if (status[1]) break;
+ if (poll == 99) $fatal(1, "configuration FLUSH did not enter drain state");
end
// Pair each AW with its B response to avoid filling the serializer's
@@ -81,7 +111,7 @@ module axi_pre_aw_drain_tb;
end
end
- wait (complete_config_done);
+ wait_barrier_idle();
for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin
fix.ar_beat.ax_addr = BaseAddress + transaction * AxiDataBytes;
@@ -100,11 +130,10 @@ module axi_pre_aw_drain_tb;
end
// Start a burst longer than the host W FIFO before presenting its AW. The
- // sender must stop at the 16-beat FIFO capacity while the drain barrier is
+ // sender must stop at the 16-beat FIFO capacity while the flush barrier is
// entered, then resume only after the matching AW is accepted.
partial_w_fifo_full = 1'b0;
- partial_config_busy = 1'b0;
- partial_config_done = 1'b0;
+ partial_flush_busy = 1'b0;
partial_w_done = 1'b0;
fork
begin : send_partial_w_burst
@@ -118,7 +147,8 @@ module axi_pre_aw_drain_tb;
end
partial_w_done = 1'b1;
end
- begin : config_partial_w_burst
+ begin : flush_partial_w_burst
+ logic [31:0] partial_status;
logic partial_error;
wait (partial_w_fifo_full);
@@ -127,14 +157,23 @@ module axi_pre_aw_drain_tb;
$fatal(1, "partial W burst did not reach FIFO backpressure");
end
- partial_config_busy = 1'b1;
- fix.i_rmaster.send_write(DrainConfigAddr, DrainConfigValue, '1, partial_error);
- if (partial_error) $fatal(1, "partial drain configuration write failed");
- partial_config_done = 1'b1;
+ fix.i_rmaster.send_write(32'h00c, 32'h1, '1, partial_error);
+ if (partial_error) $fatal(1, "partial configuration FLUSH write failed");
+ for (int unsigned poll = 0; poll < 100; poll++) begin
+ fix.i_rmaster.send_read(32'h010, partial_status, partial_error);
+ if (partial_error) $fatal(1, "partial configuration status read failed");
+ if (partial_status[1]) begin
+ partial_flush_busy = 1'b1;
+ break;
+ end
+ end
+ if (!partial_flush_busy) begin
+ $fatal(1, "partial configuration FLUSH did not enter drain state");
+ end
end
join_none
- wait (partial_config_busy);
+ wait (partial_flush_busy);
fix.aw_beat.ax_addr = PartialBaseAddress;
fix.aw_beat.ax_id = 6'd32;
fix.aw_beat.ax_len = PartialBurstBeats - 1;
@@ -148,7 +187,7 @@ module axi_pre_aw_drain_tb;
fix.b_beat.b_resp);
end
wait (partial_w_done);
- wait (partial_config_done);
+ wait_barrier_idle();
for (int unsigned beat = 0; beat < PartialBurstBeats; beat++) begin
fix.ar_beat.ax_addr = PartialBaseAddress + beat * AxiDataBytes;
diff --git a/test/dut_if.sv b/test/dut_if.sv
index d7485e8..49e893a 100644
--- a/test/dut_if.sv
+++ b/test/dut_if.sv
@@ -162,6 +162,7 @@ module dut_if
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_resp_t ),
.RegDataWidth ( RegDw ),
+ .RegAddrWidth ( RegAw ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.axi_rule_t ( axi_rule_t )
diff --git a/test/hyperbus_cfg_regs_tb.sv b/test/hyperbus_cfg_regs_tb.sv
index ba58dd7..ec96707 100644
--- a/test/hyperbus_cfg_regs_tb.sv
+++ b/test/hyperbus_cfg_regs_tb.sv
@@ -3,7 +3,6 @@
// SPDX-License-Identifier: SHL-0.51
module hyperbus_cfg_regs_tb;
-
typedef struct packed {
logic [31:0] addr;
logic write;
@@ -11,176 +10,174 @@ module hyperbus_cfg_regs_tb;
logic [3:0] wstrb;
logic valid;
} reg_req_t;
-
typedef struct packed {
logic [31:0] rdata;
logic error;
logic ready;
} reg_rsp_t;
-
typedef struct packed {
logic [31:0] start_addr;
logic [31:0] end_addr;
logic [2:0] idx;
} addr_rule_t;
- logic clk;
- logic rst_n;
- reg_req_t reg_req;
- reg_rsp_t reg_rsp;
- hyperbus_pkg::frontend_cfg_t frontend_cfg;
- hyperbus_pkg::phy_cfg_t phy_cfg;
+ logic clk, rst_n;
+ reg_req_t reg_req;
+ reg_rsp_t reg_rsp;
+ logic status_busy, status_dirty, decode_error;
+ logic command_flush, command_apply;
+ int unsigned command_pulse_count;
+ hyperbus_pkg::frontend_cfg_t frontend_cfg;
+ hyperbus_pkg::phy_cfg_t phy_cfg;
addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules;
- logic decode_error;
- task automatic reg_write(
- input logic [31:0] addr,
- input logic [31:0] data,
- input logic inject_decode_error
- );
+ task automatic reg_write(input logic [31:0] addr, input logic [31:0] data);
+ begin
@(negedge clk);
- reg_req.addr = addr;
- reg_req.write = 1'b1;
- reg_req.wdata = data;
- reg_req.wstrb = '1;
- reg_req.valid = 1'b1;
- decode_error = inject_decode_error;
-
- do begin
- @(posedge clk);
- end while (!reg_rsp.ready);
- if (reg_rsp.error) begin
- $error("Register write to 0x%0h failed", addr);
- end
-
+ reg_req <= '{addr: addr, write: 1'b1, wdata: data, wstrb: 4'hf, valid: 1'b1};
+ do begin @(posedge clk); end while (!reg_rsp.ready);
+ if (reg_rsp.error) $fatal(1, "write failed at %h", addr);
@(negedge clk);
- reg_req.valid = 1'b0;
- decode_error = 1'b0;
+ reg_req.valid <= 1'b0;
+ end
endtask
- task automatic reg_read(
- input logic [31:0] addr,
- output logic [31:0] data
- );
+ task automatic reg_read(input logic [31:0] addr, output logic [31:0] data);
+ begin
@(negedge clk);
- reg_req.addr = addr;
- reg_req.write = 1'b0;
- reg_req.wdata = '0;
- reg_req.wstrb = '0;
- reg_req.valid = 1'b1;
-
- do begin
- @(posedge clk);
- end while (!reg_rsp.ready);
+ reg_req <= '{addr: addr, write: 1'b0, wdata: '0, wstrb: '0, valid: 1'b1};
+ do begin @(posedge clk); end while (!reg_rsp.ready);
data = reg_rsp.rdata;
- if (reg_rsp.error) begin
- $error("Register read from 0x%0h failed", addr);
- end
+ if (reg_rsp.error) $fatal(1, "read failed at %h", addr);
+ @(negedge clk);
+ reg_req.valid <= 1'b0;
+ end
+ endtask
+ task automatic reg_write_error(input logic [31:0] addr, input logic [31:0] data,
+ input logic [3:0] strb);
+ begin
+ @(negedge clk);
+ reg_req <= '{addr: addr, write: 1'b1, wdata: data, wstrb: strb, valid: 1'b1};
+ do begin @(posedge clk); end while (!reg_rsp.ready);
+ if (!reg_rsp.error) $error("invalid write to %h was accepted", addr);
@(negedge clk);
- reg_req.valid = 1'b0;
+ reg_req.valid <= 1'b0;
+ end
endtask
- initial begin
- clk = 1'b0;
- forever #5ns clk = ~clk;
- end
+ task automatic reg_read_error(input logic [31:0] addr);
+ begin
+ @(negedge clk);
+ reg_req <= '{addr: addr, write: 1'b0, wdata: '0, wstrb: '0, valid: 1'b1};
+ do begin @(posedge clk); end while (!reg_rsp.ready);
+ if (!reg_rsp.error) $error("unmapped read from %h was accepted", addr);
+ @(negedge clk);
+ reg_req.valid <= 1'b0;
+ end
+ endtask
- sim_timeout #(
- .Cycles ( 100 )
- ) i_sim_timeout (
- .clk_i ( clk ),
- .rst_ni ( rst_n )
- );
+ initial begin clk = 1'b0; forever #5ns clk = ~clk; end
+
+ always @(posedge clk) begin
+ if (rst_n && command_flush && command_apply) command_pulse_count++;
+ end
hyperbus_cfg_regs #(
- .NumPhys ( 2 ),
- .RegDataWidth ( 32 ),
- .reg_req_t ( reg_req_t ),
- .reg_rsp_t ( reg_rsp_t ),
+ .NumPhys ( 2 ),
+ .RegDataWidth ( 32 ),
+ .RegAddrWidth ( 32 ),
+ .ClockDividerImplemented ( 1'b1 ),
+ .reg_req_t ( reg_req_t ),
+ .reg_rsp_t ( reg_rsp_t ),
.addr_rule_t ( addr_rule_t )
) i_cfg_regs (
- .clk_i ( clk ),
- .rst_ni ( rst_n ),
- .reg_req_i ( reg_req ),
- .reg_rsp_o ( reg_rsp ),
- .frontend_cfg_o ( frontend_cfg ),
- .phy_cfg_o ( phy_cfg ),
- .chip_rules_o ( chip_rules ),
- .decode_error_i ( decode_error )
+ .clk_i ( clk ),
+ .rst_ni ( rst_n ),
+ .reg_req_i ( reg_req ),
+ .reg_rsp_o ( reg_rsp ),
+ .status_busy_i ( status_busy ),
+ .status_dirty_i ( status_dirty ),
+ .decode_error_i ( decode_error ),
+ .command_flush_o( command_flush ),
+ .command_apply_o( command_apply ),
+ .frontend_cfg_o ( frontend_cfg ),
+ .phy_cfg_o ( phy_cfg ),
+ .chip_rules_o ( chip_rules )
);
initial begin : proc_stimulus
- logic [31:0] status;
- logic [31:0] capability;
- logic [31:0] chip_addr;
- logic [31:0] clock_div;
- logic [31:0] rx_clk_delay;
- logic [31:0] tx_clk_delay;
-
- rst_n = 1'b0;
- reg_req = '0;
+ logic [31:0] data;
+ rst_n = 1'b0;
+ reg_req = '0;
+ status_busy = 1'b0;
+ status_dirty = 1'b0;
decode_error = 1'b0;
- repeat (5) @(posedge clk);
- @(negedge clk);
+ command_pulse_count = 0;
+ repeat (4) @(posedge clk);
rst_n = 1'b1;
- if ((chip_rules[0].start_addr != 32'h0000_0000) ||
- (chip_rules[0].end_addr != 32'h0040_0000) ||
- (chip_rules[7].start_addr != 32'h01c0_0000) ||
+ reg_read(32'h000, data);
+ if (data != 32'h0000_0900) $fatal(1, "IP version reset mismatch: %h", data);
+ reg_read(32'h004, data);
+ if (data != 32'h0001_0000) $error("REG_IF version reset mismatch: %h", data);
+ reg_read(32'h008, data);
+ if (data != 32'h003f_0208) $error("capability fields mismatch: %h", data);
+ reg_read(32'h400, data);
+ if (data != 32'h0000_0000) $fatal(1, "chip0 base reset mismatch");
+ reg_read(32'h418, data);
+ if (data != 32'h0000_0010) $fatal(1, "chip0 rx delay reset mismatch");
+ if ((chip_rules[7].start_addr != 32'h01c0_0000) ||
(chip_rules[7].end_addr != 32'h0200_0000)) begin
- $error("Unexpected reset chip address map");
- end
-
- reg_read(32'h008, capability);
- if (capability != 32'h0020_0208) begin
- $error("Unexpected implementation capability bits: %h", capability);
- end
-
- reg_read(32'h200, clock_div);
- if ((clock_div != 8) || (frontend_cfg.phy_clock_div != 8)) begin
- $error("Unexpected PHY clock divider reset value");
- end
-
- reg_write(32'h200, 8'd4, 1'b0);
- reg_read(32'h200, clock_div);
- if ((clock_div != 4) || (frontend_cfg.phy_clock_div != 4)) begin
- $error("PHY clock divider write did not take effect");
+ $error("chip7 reset range mismatch");
end
- // Exercise both the fine [4:0] and coarse [7:5] delay settings.
- reg_write(32'h418, 8'hb5, 1'b0);
- reg_write(32'h300, 8'h6a, 1'b0);
- reg_read(32'h418, rx_clk_delay);
- reg_read(32'h300, tx_clk_delay);
- if ((rx_clk_delay != 8'hb5) || (tx_clk_delay != 8'h6a) ||
- (phy_cfg.chip.t_rx_clk_delay != 8'hb5) ||
- (phy_cfg.t_tx_clk_delay != 8'h6a)) begin
- $error("Delay-line configuration fields did not take effect");
+ reg_write(32'h440, 32'h8100_0000);
+ reg_read(32'h440, data);
+ if (data != 32'h8100_0000) $fatal(1, "chip1 base alignment mismatch: %h", data);
+ if (frontend_cfg.chip[1].range_base != 32'h8100_0000) begin
+ $error("chip1 frontend mapping mismatch");
end
-
- // Only address bits [31:22] are stored; low bits read back as zero.
- reg_write(32'h5c0, 32'h8134_5678, 1'b0);
- reg_read(32'h5c0, chip_addr);
- if ((chip_addr != 32'h8100_0000) ||
- (chip_rules[7].start_addr != 32'h8100_0000)) begin
- $error("Chip address alignment was not enforced");
+ reg_write(32'h340, 32'h0000_00a5);
+ if (phy_cfg.phy[1].tx_delay != 8'ha5 || phy_cfg.phy[0].tx_delay != 8'h10) begin
+ $error("per-PHY delay mapping mismatch");
end
-
- // A new hardware event must win over a simultaneous software W1C.
- reg_write(32'h010, 32'h1, 1'b1);
- reg_read(32'h010, status);
- if (!status[0]) begin
- $error("Decode error was lost during a simultaneous W1C");
+ reg_write(32'h44c, 32'h0001_0307);
+ if ((phy_cfg.chip[1].t_latency_access != 4'd7) ||
+ (phy_cfg.chip[1].rwds_sample_delay != 4'd3) ||
+ !phy_cfg.chip[1].en_latency_additional) begin
+ $error("per-chip latency mapping mismatch");
end
- reg_write(32'h010, 32'h1, 1'b0);
- reg_read(32'h010, status);
- if (status[0]) begin
- $error("Decode error did not clear without a new hardware event");
+ reg_write_error(32'h200, 32'h1, 4'b0001);
+ reg_read(32'h200, data);
+ if (data != 32'h8) $error("invalid divider write changed the register: %h", data);
+ reg_write_error(32'h44c, 32'h0000_0002, 4'b0001);
+ reg_read(32'h44c, data);
+ if (data != 32'h0001_0307) $error("invalid latency write changed the register: %h", data);
+ reg_read_error(32'h104);
+ reg_read_error(32'h1000);
+
+ reg_write(32'h00c, 32'h3);
+ repeat (2) @(posedge clk);
+ #1ns;
+ if (command_pulse_count != 1) begin
+ $error("command strobes were sampled %0d times", command_pulse_count);
end
-
+ if (command_apply || command_flush) $error("command strobes did not clear");
+
+ status_busy = 1'b1;
+ status_dirty = 1'b1;
+ decode_error = 1'b1;
+ reg_read(32'h010, data);
+ if ((data & 32'h7) != 32'h7) $error("live status mismatch: %h", data);
+ reg_write(32'h010, 32'h1);
+ reg_read(32'h010, data);
+ if (!data[0]) $error("hardware set did not win over W1C");
+ decode_error = 1'b0;
+ reg_write(32'h010, 32'h1);
+ reg_read(32'h010, data);
+ if (data[0]) $error("decode error did not clear");
$finish;
end
-
endmodule
diff --git a/test/hyperbus_test_dut.sv b/test/hyperbus_test_dut.sv
index d897c81..39d27a9 100644
--- a/test/hyperbus_test_dut.sv
+++ b/test/hyperbus_test_dut.sv
@@ -3,19 +3,20 @@
// SPDX-License-Identifier: SHL-0.51
module hyperbus_test_dut #(
- parameter int unsigned DutVariant = 0,
- parameter int unsigned NumPhys = 2,
- parameter int unsigned AxiAddrWidth = -1,
- parameter int unsigned AxiDataWidth = -1,
- parameter int unsigned AxiIdWidth = -1,
- parameter int unsigned AxiUserWidth = -1,
- parameter type axi_req_t = logic,
- parameter type axi_rsp_t = logic,
- parameter int unsigned RegDataWidth = -1,
- parameter int unsigned HostWriteBufferBytes = 64,
- parameter type reg_req_t = logic,
- parameter type reg_rsp_t = logic,
- parameter type axi_rule_t = logic
+ parameter int unsigned DutVariant = 0,
+ parameter int unsigned NumPhys = 2,
+ parameter int unsigned AxiAddrWidth = -1,
+ parameter int unsigned AxiDataWidth = -1,
+ parameter int unsigned AxiIdWidth = -1,
+ parameter int unsigned AxiUserWidth = -1,
+ parameter type axi_req_t = logic,
+ parameter type axi_rsp_t = logic,
+ parameter int unsigned RegDataWidth = -1,
+ parameter int unsigned RegAddrWidth = 32,
+ parameter int unsigned HostWriteBufferBytes = 64,
+ parameter type reg_req_t = logic,
+ parameter type reg_rsp_t = logic,
+ parameter type axi_rule_t = logic
) (
input logic clk_sys_i,
input logic rst_sys_ni,
@@ -59,6 +60,7 @@ module hyperbus_test_dut #(
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t ),
.RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.axi_rule_t ( axi_rule_t )
@@ -95,6 +97,7 @@ module hyperbus_test_dut #(
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t ),
.RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.axi_rule_t ( axi_rule_t )
@@ -131,6 +134,7 @@ module hyperbus_test_dut #(
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t ),
.RegDataWidth ( RegDataWidth ),
+ .RegAddrWidth ( RegAddrWidth ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t ),
.axi_rule_t ( axi_rule_t )