diff --git a/docs/cpuif/internal_protocol.rst b/docs/cpuif/internal_protocol.rst index 3b34956..a49c558 100644 --- a/docs/cpuif/internal_protocol.rst +++ b/docs/cpuif/internal_protocol.rst @@ -34,6 +34,10 @@ cpuif_wr_data Data to be written for the write transfer. This signal is ignored for read transfers. +cpuif_wr_biten + Active-high bit-level write-enable strobes. + Only asserted bit positions will change the register value during a write transfer. + cpuif_req_stall_rd If asserted, and the next pending request is a read operation, then the transfer will not be accepted until this signal is deasserted. diff --git a/docs/dev_notes/Validation Needed b/docs/dev_notes/Validation Needed index 5c2ac3d..4fab5f0 100644 --- a/docs/dev_notes/Validation Needed +++ b/docs/dev_notes/Validation Needed @@ -173,6 +173,10 @@ X Do not allow unaligned addresses ! Error if a property references a non-signal component, or property reference from outside the export hierarchy +! Error if a property is a reference to something that is external, or enclosed + in an external component. + Limit this check to child nodes inside the export hierarchy + ! Add warning for sticky race condition stickybit and other similar situations generally should use hw precedence. Emit a warning as appropriate diff --git a/src/peakrdl_regblock/cpuif/apb3/apb3_tmpl.sv b/src/peakrdl_regblock/cpuif/apb3/apb3_tmpl.sv index 983f337..bf28783 100644 --- a/src/peakrdl_regblock/cpuif/apb3/apb3_tmpl.sv +++ b/src/peakrdl_regblock/cpuif/apb3/apb3_tmpl.sv @@ -28,6 +28,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin end end end +assign cpuif_wr_biten = '1; // Response assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack; diff --git a/src/peakrdl_regblock/cpuif/axi4lite/axi4lite_tmpl.sv b/src/peakrdl_regblock/cpuif/axi4lite/axi4lite_tmpl.sv index 20a87f8..2da192e 100644 --- a/src/peakrdl_regblock/cpuif/axi4lite/axi4lite_tmpl.sv +++ b/src/peakrdl_regblock/cpuif/axi4lite/axi4lite_tmpl.sv @@ -8,6 +8,7 @@ logic axil_awvalid; logic [{{cpuif.addr_width-1}}:0] axil_awaddr; logic axil_wvalid; logic [{{cpuif.data_width-1}}:0] axil_wdata; +logic [{{cpuif.data_width-1}}:0] axil_wstrb; logic axil_aw_accept; logic axil_resp_acked; @@ -21,6 +22,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin axil_awaddr <= '0; axil_wvalid <= '0; axil_wdata <= '0; + axil_wstrb <= '0; axil_n_in_flight <= '0; end else begin // AR* acceptance register @@ -46,6 +48,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin if({{cpuif.signal("wvalid")}} && {{cpuif.signal("wready")}}) begin axil_wvalid <= '1; axil_wdata <= {{cpuif.signal("wdata")}}; + axil_wstrb <= {{cpuif.signal("wstrb")}}; end // Keep track of in-flight transactions @@ -66,6 +69,9 @@ end // Request dispatch always_comb begin cpuif_wr_data = axil_wdata; + for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin + cpuif_wr_biten[i*8 +: 8] = {8{axil_wstrb[i]}}; + end cpuif_req = '0; cpuif_req_is_wr = '0; cpuif_addr = '0; diff --git a/src/peakrdl_regblock/cpuif/passthrough/__init__.py b/src/peakrdl_regblock/cpuif/passthrough/__init__.py index cfbeeee..0363860 100644 --- a/src/peakrdl_regblock/cpuif/passthrough/__init__.py +++ b/src/peakrdl_regblock/cpuif/passthrough/__init__.py @@ -10,6 +10,7 @@ class PassthroughCpuif(CpuifBase): "input wire s_cpuif_req_is_wr", f"input wire [{self.addr_width-1}:0] s_cpuif_addr", f"input wire [{self.data_width-1}:0] s_cpuif_wr_data", + f"input wire [{self.data_width-1}:0] s_cpuif_wr_biten", "output wire s_cpuif_req_stall_wr", "output wire s_cpuif_req_stall_rd", "output wire s_cpuif_rd_ack", diff --git a/src/peakrdl_regblock/cpuif/passthrough/passthrough_tmpl.sv b/src/peakrdl_regblock/cpuif/passthrough/passthrough_tmpl.sv index b0897fc..8e5bb70 100644 --- a/src/peakrdl_regblock/cpuif/passthrough/passthrough_tmpl.sv +++ b/src/peakrdl_regblock/cpuif/passthrough/passthrough_tmpl.sv @@ -2,6 +2,7 @@ assign cpuif_req = s_cpuif_req; assign cpuif_req_is_wr = s_cpuif_req_is_wr; assign cpuif_addr = s_cpuif_addr; assign cpuif_wr_data = s_cpuif_wr_data; +assign cpuif_wr_biten = s_cpuif_wr_biten; assign s_cpuif_req_stall_wr = cpuif_req_stall_wr; assign s_cpuif_req_stall_rd = cpuif_req_stall_rd; assign s_cpuif_rd_ack = cpuif_rd_ack; diff --git a/src/peakrdl_regblock/field_logic/sw_onwrite.py b/src/peakrdl_regblock/field_logic/sw_onwrite.py index f9ef6d7..0d2c6c9 100644 --- a/src/peakrdl_regblock/field_logic/sw_onwrite.py +++ b/src/peakrdl_regblock/field_logic/sw_onwrite.py @@ -33,14 +33,25 @@ class _OnWrite(NextStateConditional): value = f"decoded_wr_data[{field.high}:{field.low}]" return value + def _wr_biten(self, field: 'FieldNode') -> str: + if field.msb < field.lsb: + # Field gets bitswapped since it is in [low:high] orientation + value = f"{{<<{{decoded_wr_biten[{field.high}:{field.low}]}}}}" + else: + value = f"decoded_wr_biten[{field.high}:{field.low}]" + return value + + class WriteOneSet(_OnWrite): comment = "SW write 1 set" onwritetype = OnWriteType.woset def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} | {self._wr_data(field)};", + f"next_c = {R} | ({D} & {S});", "load_next_c = '1;", ] @@ -50,8 +61,10 @@ class WriteOneClear(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} & ~{self._wr_data(field)};", + f"next_c = {R} & ~({D} & {S});", "load_next_c = '1;", ] @@ -61,8 +74,10 @@ class WriteOneToggle(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} ^ {self._wr_data(field)};", + f"next_c = {R} ^ ({D} & {S});", "load_next_c = '1;", ] @@ -72,8 +87,10 @@ class WriteZeroSet(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} | ~{self._wr_data(field)};", + f"next_c = {R} | (~{D} & {S});", "load_next_c = '1;", ] @@ -83,8 +100,10 @@ class WriteZeroClear(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} & {self._wr_data(field)};", + f"next_c = {R} & ({D} | ~{S});", "load_next_c = '1;", ] @@ -94,8 +113,10 @@ class WriteZeroToggle(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {R} ^ ~{self._wr_data(field)};", + f"next_c = {R} ^ (~{D} & {S});", "load_next_c = '1;", ] @@ -124,7 +145,10 @@ class Write(_OnWrite): onwritetype = None def get_assignments(self, field: 'FieldNode') -> List[str]: + R = self.exp.field_logic.get_storage_identifier(field) + D = self._wr_data(field) + S = self._wr_biten(field) return [ - f"next_c = {self._wr_data(field)};", + f"next_c = ({R} & ~{S}) | ({D} & {S});", "load_next_c = '1;", ] diff --git a/src/peakrdl_regblock/module_tmpl.sv b/src/peakrdl_regblock/module_tmpl.sv index 8acff50..c5d84a2 100644 --- a/src/peakrdl_regblock/module_tmpl.sv +++ b/src/peakrdl_regblock/module_tmpl.sv @@ -26,6 +26,7 @@ module {{module_name}} ( logic cpuif_req_is_wr; logic [{{cpuif.addr_width-1}}:0] cpuif_addr; logic [{{cpuif.data_width-1}}:0] cpuif_wr_data; + logic [{{cpuif.data_width-1}}:0] cpuif_wr_biten; logic cpuif_req_stall_wr; logic cpuif_req_stall_rd; @@ -84,6 +85,7 @@ module {{module_name}} ( logic decoded_req; logic decoded_req_is_wr; logic [{{cpuif.data_width-1}}:0] decoded_wr_data; + logic [{{cpuif.data_width-1}}:0] decoded_wr_biten; always_comb begin {{address_decode.get_implementation()|indent(8)}} @@ -93,6 +95,7 @@ module {{module_name}} ( assign decoded_req = cpuif_req_masked; assign decoded_req_is_wr = cpuif_req_is_wr; assign decoded_wr_data = cpuif_wr_data; + assign decoded_wr_biten = cpuif_wr_biten; // Writes are always granted with no error response assign cpuif_wr_ack = decoded_req & decoded_req_is_wr; diff --git a/tests/lib/cpuifs/passthrough/passthrough_driver.sv b/tests/lib/cpuifs/passthrough/passthrough_driver.sv index e0ee464..9145172 100644 --- a/tests/lib/cpuifs/passthrough/passthrough_driver.sv +++ b/tests/lib/cpuifs/passthrough/passthrough_driver.sv @@ -9,6 +9,7 @@ interface passthrough_driver #( output logic m_cpuif_req_is_wr, output logic [ADDR_WIDTH-1:0] m_cpuif_addr, output logic [DATA_WIDTH-1:0] m_cpuif_wr_data, + output logic [DATA_WIDTH-1:0] m_cpuif_wr_biten, input wire m_cpuif_req_stall_wr, input wire m_cpuif_req_stall_rd, input wire m_cpuif_rd_ack, @@ -27,6 +28,7 @@ interface passthrough_driver #( output m_cpuif_req_is_wr; output m_cpuif_addr; output m_cpuif_wr_data; + output m_cpuif_wr_biten; input m_cpuif_req_stall_wr; input m_cpuif_req_stall_rd; input m_cpuif_rd_ack; @@ -41,12 +43,13 @@ interface passthrough_driver #( cb.m_cpuif_req_is_wr <= '0; cb.m_cpuif_addr <= '0; cb.m_cpuif_wr_data <= '0; + cb.m_cpuif_wr_biten <= '0; endtask semaphore txn_req_mutex = new(1); semaphore txn_resp_mutex = new(1); - task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data); + task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH-1:0] biten = '1); fork begin // Initiate transfer @@ -56,6 +59,7 @@ interface passthrough_driver #( cb.m_cpuif_req_is_wr <= '1; cb.m_cpuif_addr <= addr; cb.m_cpuif_wr_data <= data; + cb.m_cpuif_wr_biten <= biten; @(cb); while(cb.m_cpuif_req_stall_wr !== 1'b0) @(cb); reset(); diff --git a/tests/lib/cpuifs/passthrough/tb_inst.sv b/tests/lib/cpuifs/passthrough/tb_inst.sv index 680b794..e33b9fe 100644 --- a/tests/lib/cpuifs/passthrough/tb_inst.sv +++ b/tests/lib/cpuifs/passthrough/tb_inst.sv @@ -3,6 +3,7 @@ wire s_cpuif_req; wire s_cpuif_req_is_wr; wire [{{exporter.cpuif.addr_width-1}}:0] s_cpuif_addr; wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_data; +wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_biten; wire s_cpuif_req_stall_wr; wire s_cpuif_req_stall_rd; wire s_cpuif_rd_ack; @@ -20,6 +21,7 @@ passthrough_driver #( .m_cpuif_req_is_wr(s_cpuif_req_is_wr), .m_cpuif_addr(s_cpuif_addr), .m_cpuif_wr_data(s_cpuif_wr_data), + .m_cpuif_wr_biten(s_cpuif_wr_biten), .m_cpuif_req_stall_wr(s_cpuif_req_stall_wr), .m_cpuif_req_stall_rd(s_cpuif_req_stall_rd), .m_cpuif_rd_ack(s_cpuif_rd_ack), diff --git a/tests/test_write_strobes/__init__.py b/tests/test_write_strobes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_write_strobes/regblock.rdl b/tests/test_write_strobes/regblock.rdl new file mode 100644 index 0000000..bfa31bd --- /dev/null +++ b/tests/test_write_strobes/regblock.rdl @@ -0,0 +1,54 @@ +addrmap top { + + reg { + field { + sw=rw; hw=na; + onwrite = woset; + } f1[3:0] = 0x0; + + field { + sw=rw; hw=na; + onwrite = woclr; + } f2[7:4] = 0xF; + + field { + sw=rw; hw=na; + onwrite = wot; + } f3[11:8] = 0x0; + } r1; + + reg { + field { + sw=rw; hw=na; + onwrite = wzs; + } f1[3:0] = 0x0; + + field { + sw=rw; hw=na; + onwrite = wzc; + } f2[7:4] = 0xF; + + field { + sw=rw; hw=na; + onwrite = wzt; + } f3[11:8] = 0x0; + } r2; + + reg { + field { + sw=rw; hw=na; + onwrite = wclr; + } f1[7:0] = 0xF0; + + field { + sw=rw; hw=na; + onwrite = wset; + } f2[15:8] = 0x0F; + } r3; + + reg { + field { + sw=rw; hw=na; + } f3[7:0] = 0x00; + } r4; +}; diff --git a/tests/test_write_strobes/tb_template.sv b/tests/test_write_strobes/tb_template.sv new file mode 100644 index 0000000..12e8e57 --- /dev/null +++ b/tests/test_write_strobes/tb_template.sv @@ -0,0 +1,31 @@ +{% extends "lib/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + cpuif.assert_read('h0, 'h0_F_0); + cpuif.write ('h0, 'h5_5_5, 'h3_3_3); + cpuif.assert_read('h0, 'h1_E_1); + cpuif.write ('h0, 'h5_A_A, 'h3_3_3); + cpuif.assert_read('h0, 'h0_C_3); + + cpuif.assert_read('h4, 'h0_F_0); + cpuif.write ('h4, 'hA_A_A, 'h3_3_3); + cpuif.assert_read('h4, 'h1_E_1); + cpuif.write ('h4, 'hA_5_5, 'h3_3_3); + cpuif.assert_read('h4, 'h0_C_3); + + cpuif.assert_read('h8, 'h0F_F0); + cpuif.write ('h8, 'h12_34, 'hFF_00); + cpuif.assert_read('h8, 'hFF_00); + + cpuif.assert_read('hC, 'h00); + cpuif.write ('hC, 'hFF, 'hF0); + cpuif.assert_read('hC, 'hF0); + cpuif.write ('hC, 'h00, 'h3C); + cpuif.assert_read('hC, 'hC0); + +{% endblock %} diff --git a/tests/test_write_strobes/testcase.py b/tests/test_write_strobes/testcase.py new file mode 100644 index 0000000..b4c56ed --- /dev/null +++ b/tests/test_write_strobes/testcase.py @@ -0,0 +1,9 @@ +from ..lib.sim_testcase import SimTestCase + +from ..lib.cpuifs.passthrough import Passthrough + +class Test(SimTestCase): + cpuif = Passthrough() + + def test_dut(self): + self.run_test()