diff --git a/docs/rdl_features/external.rst b/docs/rdl_features/external.rst index 5137a73..b2d62d9 100644 --- a/docs/rdl_features/external.rst +++ b/docs/rdl_features/external.rst @@ -44,10 +44,16 @@ hwif_out..req When asserted, a read or write transfer will be initiated. Qualifies all other request signals. - If a register is wide (``regwidth`` > ``accesswidth``), then the + If the register is wide (``regwidth`` > ``accesswidth``), then the ``hwif_out..req`` will consist of multiple bits, representing the access strobe for each sub-word of the register. + If the register does not contain any readable fields, this strobe will be + suppressed for read operations. + + If the register does not contain any writable readable fields, this strobe + will be suppressed for write operations. + hwif_out..req_is_wr If ``1``, denotes that the current transfer is a write. Otherwise transfer is a read. @@ -59,11 +65,15 @@ hwif_out..wr_data The bit-width of this signal always matches the CPUIF's bus width, regardless of the regwidth. + If the register does not contain any writable fields, this signal is omitted. + hwif_out..wr_biten Active-high bit-level write-enable strobes. Only asserted bit positions will change the register value during a write transfer. + If the register does not contain any writable fields, this signal is omitted. + Read Response ^^^^^^^^^^^^^ @@ -74,9 +84,13 @@ hwif_in..rd_ack If the transfer is always completed in the same cycle, it is acceptable to tie this signal to ``hwif_out..req && !hwif_out..req_is_wr``. + If the register does not contain any readable fields, this signal is omitted. + hwif_in..rd_data Read response data. + If the register does not contain any readable fields, this signal is omitted. + Write Response ^^^^^^^^^^^^^^ hwif_in..wr_ack @@ -85,6 +99,8 @@ hwif_in..wr_ack If the transfer is always completed in the same cycle, it is acceptable to tie this signal to ``hwif_out..req && hwif_out..req_is_wr``. + If the register does not contain any writable fields, this signal is omitted. + External Blocks diff --git a/src/peakrdl_regblock/addr_decode.py b/src/peakrdl_regblock/addr_decode.py index 64082de..684d1b6 100644 --- a/src/peakrdl_regblock/addr_decode.py +++ b/src/peakrdl_regblock/addr_decode.py @@ -184,7 +184,16 @@ class DecodeLogicGenerator(RDLForLoopGenerator): s = f"{self.addr_decode.get_access_strobe(node)} = {rhs};" self.add_content(s) if node.external: - self.add_content(f"is_external |= {rhs};") + readable = node.has_sw_readable + writable = node.has_sw_writable + if readable and writable: + self.add_content(f"is_external |= {rhs};") + elif readable and not writable: + self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;") + elif not readable and writable: + self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;") + else: + raise RuntimeError else: # Register is wide. Create a substrobe for each subword n_subwords = regwidth // accesswidth @@ -194,7 +203,16 @@ class DecodeLogicGenerator(RDLForLoopGenerator): s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = {rhs};" self.add_content(s) if node.external: - self.add_content(f"is_external |= {rhs};") + readable = node.has_sw_readable + writable = node.has_sw_writable + if readable and writable: + self.add_content(f"is_external |= {rhs};") + elif readable and not writable: + self.add_content(f"is_external |= {rhs} & !cpuif_req_is_wr;") + elif not readable and writable: + self.add_content(f"is_external |= {rhs} & cpuif_req_is_wr;") + else: + raise RuntimeError def exit_AddressableComponent(self, node: 'AddressableNode') -> None: super().exit_AddressableComponent(node) diff --git a/src/peakrdl_regblock/external_acks.py b/src/peakrdl_regblock/external_acks.py index 966c05d..9a6c044 100644 --- a/src/peakrdl_regblock/external_acks.py +++ b/src/peakrdl_regblock/external_acks.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from systemrdl.walker import WalkerAction +from systemrdl.node import RegNode from .forloop_generator import RDLForLoopGenerator @@ -24,7 +25,8 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator): super().enter_AddressableComponent(node) if node.external: - self.add_content(f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};") + if not isinstance(node, RegNode) or node.has_sw_writable: + self.add_content(f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};") return WalkerAction.SkipDescendants return WalkerAction.Continue @@ -45,7 +47,8 @@ class ExternalReadAckGenerator(RDLForLoopGenerator): super().enter_AddressableComponent(node) if node.external: - self.add_content(f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};") + if not isinstance(node, RegNode) or node.has_sw_readable: + self.add_content(f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};") return WalkerAction.SkipDescendants return WalkerAction.Continue diff --git a/src/peakrdl_regblock/field_logic/generators.py b/src/peakrdl_regblock/field_logic/generators.py index e334fdb..00073c9 100644 --- a/src/peakrdl_regblock/field_logic/generators.py +++ b/src/peakrdl_regblock/field_logic/generators.py @@ -347,6 +347,8 @@ class FieldLogicGenerator(RDLForLoopGenerator): bslice = "" context = { + "has_sw_writable": node.has_sw_writable, + "has_sw_readable": node.has_sw_readable, "prefix": prefix, "strb": strb, "bslice": bslice, diff --git a/src/peakrdl_regblock/field_logic/templates/external_reg.sv b/src/peakrdl_regblock/field_logic/templates/external_reg.sv index d8646de..1a47515 100644 --- a/src/peakrdl_regblock/field_logic/templates/external_reg.sv +++ b/src/peakrdl_regblock/field_logic/templates/external_reg.sv @@ -5,13 +5,23 @@ always_ff {{get_always_ff_event(resetsignal)}} begin if({{get_resetsignal(resetsignal)}}) begin {{prefix}}.req <= '0; {{prefix}}.req_is_wr <= '0; + {%- if has_sw_writable %} {{prefix}}.wr_data <= '0; {{prefix}}.wr_biten <= '0; + {%- endif %} end else begin + {%- if has_sw_readable and has_sw_writable %} {{prefix}}.req <= {{strb}}; + {%- elif has_sw_readable and not has_sw_writable %} + {{prefix}}.req <= !decoded_req_is_wr ? {{strb}} : '0; + {%- elif not has_sw_readable and has_sw_writable %} + {{prefix}}.req <= decoded_req_is_wr ? {{strb}} : '0; + {%- endif %} {{prefix}}.req_is_wr <= decoded_req_is_wr; + {%- if has_sw_writable %} {{prefix}}.wr_data <= decoded_wr_data{{bslice}}; {{prefix}}.wr_biten <= decoded_wr_biten{{bslice}}; + {%- endif %} end end @@ -19,10 +29,18 @@ end {%- else -%} +{%- if has_sw_readable and has_sw_writable %} assign {{prefix}}.req = {{strb}}; +{%- elif has_sw_readable and not has_sw_writable %} +assign {{prefix}}.req = !decoded_req_is_wr ? {{strb}} : '0; +{%- elif not has_sw_readable and has_sw_writable %} +assign {{prefix}}.req = decoded_req_is_wr ? {{strb}} : '0; +{%- endif %} assign {{prefix}}.req_is_wr = decoded_req_is_wr; +{%- if has_sw_writable %} assign {{prefix}}.wr_data = decoded_wr_data{{bslice}}; assign {{prefix}}.wr_biten = decoded_wr_biten{{bslice}}; +{%- endif %} {%- endif %} diff --git a/src/peakrdl_regblock/hwif/generators.py b/src/peakrdl_regblock/hwif/generators.py index 6c7ae68..f67dac7 100644 --- a/src/peakrdl_regblock/hwif/generators.py +++ b/src/peakrdl_regblock/hwif/generators.py @@ -97,9 +97,11 @@ class InputStructGenerator_Hier(HWIFStructGenerator): super().enter_Reg(node) if node.external: width = min(self.hwif.ds.cpuif_data_width, node.get_property('regwidth')) - self.add_member("rd_ack") - self.add_member("rd_data", width) - self.add_member("wr_ack") + if node.has_sw_readable: + self.add_member("rd_ack") + self.add_member("rd_data", width) + if node.has_sw_writable: + self.add_member("wr_ack") return WalkerAction.SkipDescendants return WalkerAction.Continue @@ -194,8 +196,9 @@ class OutputStructGenerator_Hier(HWIFStructGenerator): n_subwords = node.get_property("regwidth") // node.get_property("accesswidth") self.add_member("req", n_subwords) self.add_member("req_is_wr") - self.add_member("wr_data", width) - self.add_member("wr_biten", width) + if node.has_sw_writable: + self.add_member("wr_data", width) + self.add_member("wr_biten", width) return WalkerAction.SkipDescendants return WalkerAction.Continue diff --git a/src/peakrdl_regblock/readback/generators.py b/src/peakrdl_regblock/readback/generators.py index df8d064..59e8426 100644 --- a/src/peakrdl_regblock/readback/generators.py +++ b/src/peakrdl_regblock/readback/generators.py @@ -92,11 +92,11 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator): return WalkerAction.Continue def enter_Reg(self, node: RegNode) -> WalkerAction: - if node.external: - self.process_external_reg(node) + if not node.has_sw_readable: return WalkerAction.SkipDescendants - if not node.has_sw_readable: + if node.external: + self.process_external_reg(node) return WalkerAction.SkipDescendants accesswidth = node.get_property('accesswidth') diff --git a/tests/run.sh b/tests/run.sh index 2852d94..b6f5612 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -15,7 +15,7 @@ pip install -r $this_dir/requirements.txt # Install dut cd $this_dir/../ -python $this_dir/../setup.py install +pip install -U . cd $this_dir # Run unit tests diff --git a/tests/test_external/regblock.rdl b/tests/test_external/regblock.rdl index ba40aad..a978206 100644 --- a/tests/test_external/regblock.rdl +++ b/tests/test_external/regblock.rdl @@ -26,4 +26,26 @@ addrmap top { memwidth = 32; mementries = 8; } mm @ 0x3000; + + reg my_ro_reg { + field {sw=r; hw=w;} whatever[32] = 0; + }; + reg my_wo_reg { + field {sw=w; hw=r;} whatever[32] = 0; + }; + external my_ro_reg ro_reg @ 0x4000; + external my_wo_reg wo_reg @ 0x4004; + + reg my_wide_ro_reg { + regwidth = 64; + accesswidth = 32; + field {sw=r; hw=w;} whatever[32] = 0; + }; + reg my_wide_wo_reg { + regwidth = 64; + accesswidth = 32; + field {sw=w; hw=r;} whatever[32] = 0; + }; + external my_wide_ro_reg wide_ro_reg @ 0x4010; + external my_wide_wo_reg wide_wo_reg @ 0x4018; }; diff --git a/tests/test_external/tb_template.sv b/tests/test_external/tb_template.sv index 5f8318e..ac4cd59 100644 --- a/tests/test_external/tb_template.sv +++ b/tests/test_external/tb_template.sv @@ -95,6 +95,62 @@ .rd_data(hwif_in.mm.rd_data), .wr_ack(hwif_in.mm.wr_ack) ); + + external_reg wo_reg_inst ( + .clk(clk), + .rst(rst), + + .req(hwif_out.wo_reg.req), + .req_is_wr(hwif_out.wo_reg.req_is_wr), + .wr_data(hwif_out.wo_reg.wr_data), + .wr_biten(hwif_out.wo_reg.wr_biten), + .rd_ack(), + .rd_data(), + .wr_ack(hwif_in.wo_reg.wr_ack) + ); + + external_reg ro_reg_inst ( + .clk(clk), + .rst(rst), + + .req(hwif_out.ro_reg.req), + .req_is_wr(hwif_out.ro_reg.req_is_wr), + .wr_data(32'b0), + .wr_biten(32'b0), + .rd_ack(hwif_in.ro_reg.rd_ack), + .rd_data(hwif_in.ro_reg.rd_data), + .wr_ack() + ); + + external_reg #( + .SUBWORDS(2) + ) wide_wo_reg_inst ( + .clk(clk), + .rst(rst), + + .req(hwif_out.wide_wo_reg.req), + .req_is_wr(hwif_out.wide_wo_reg.req_is_wr), + .wr_data(hwif_out.wide_wo_reg.wr_data), + .wr_biten(hwif_out.wide_wo_reg.wr_biten), + .rd_ack(), + .rd_data(), + .wr_ack(hwif_in.wide_wo_reg.wr_ack) + ); + + external_reg #( + .SUBWORDS(2) + ) wide_ro_reg_inst ( + .clk(clk), + .rst(rst), + + .req(hwif_out.wide_ro_reg.req), + .req_is_wr(hwif_out.wide_ro_reg.req_is_wr), + .wr_data(64'b0), + .wr_biten(64'b0), + .rd_ack(hwif_in.wide_ro_reg.rd_ack), + .rd_data(hwif_in.wide_ro_reg.rd_data), + .wr_ack() + ); {%- endblock %} @@ -161,6 +217,40 @@ end end + repeat(20) begin + x = $urandom(); + ro_reg_inst.value <= x; + cpuif.write('h4000, ~x); + cpuif.assert_read('h4000, x); + assert(ro_reg_inst.value == x); + end + + repeat(20) begin + x = $urandom(); + cpuif.write('h4004, x); + cpuif.assert_read('h4004, 0); + assert(wo_reg_inst.value == x); + end + + for(int i=0; i<2; i++) begin + repeat(20) begin + x = $urandom(); + wide_ro_reg_inst.value[i] <= x; + cpuif.write('h4010 + i*4, ~x); + cpuif.assert_read('h4010 + i*4, x); + assert(wide_ro_reg_inst.value[i] == x); + end + end + + for(int i=0; i<2; i++) begin + repeat(20) begin + x = $urandom(); + cpuif.write('h4018 + i*4, x); + cpuif.assert_read('h4018 + i*4, 0); + assert(wide_wo_reg_inst.value[i] == x); + end + end + //-------------------------------------------------------------------------- // Pipelined access //--------------------------------------------------------------------------