diff --git a/doc/conf.py b/doc/conf.py index fd2482b..8704de7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,11 +14,12 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) +import datetime # -- Project information ----------------------------------------------------- project = 'PeakRDL-regblock' -copyright = '2021, Alex Mykyta' +copyright = '%d, Alex Mykyta' % datetime.datetime.now().year author = 'Alex Mykyta' @@ -44,9 +45,9 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ['_static'] diff --git a/doc/logbooks/000-Main-Logbook b/doc/logbooks/000-Main-Logbook index f058a99..825a364 100644 --- a/doc/logbooks/000-Main-Logbook +++ b/doc/logbooks/000-Main-Logbook @@ -84,22 +84,13 @@ Dev Todo list I shouldn't have to go to the hwif or whatever dereferencer should have all the query functions -- readback mux - - Start a sphinx docs thing I need to keep better track of everything! Diagrams of each layer in an architecture overview transcribe logbook into dev notes -- Am i doing msb/lsb correctly? - bit ordering: - fields are always [msb:lsb] - but could be [low:high] or [high:low] - But how does this affect bus <-> field mapping though?? - - [low:high] means that bit order gets swapped from the bus - - [high:low] means bit order is sliced naturally - DONE - + Define strict interface expectations for each layer! + Including latency/etc endianness controls byte order of the CPU bus controls byteswap at the CPUIF layer @@ -119,3 +110,7 @@ Do something about cpuif byte strobes? Generate these in the io struct? I forget what I decided - dereferencer has some remaining todos that depend on field logic + +- FIXME: cpuif reset inside top-level addrmap results in two input signals: + - one popped out to top + - another inside the input struct diff --git a/doc/logbooks/Validation Needed b/doc/logbooks/Validation Needed index b5d0ae4..53fe48a 100644 --- a/doc/logbooks/Validation Needed +++ b/doc/logbooks/Validation Needed @@ -96,7 +96,9 @@ X If a node ispresent=true, and any of it's properties are a reference, y->we = y; ... it works, but should it be allowed? Seems like user-error - +! Signals marked as field_reset or cpuif_reset need to have activehigh/activelow + specified. (8.2.1-d states that activehigh/low does not have an implied default state if unset!) + Also aplies to signals referenced by fieldreset @@ -109,19 +111,27 @@ List of stuff in case I forget. ! = No! exporter does not enforce this yet -------------------------------------------------------------------------------- + +X Contents of target are all internal. No external regs + +X Does not contain any mem components + +X Warn/error on any signal with cpuif_reset set, that is not in the top-level + addrmap. At the very least, warn that it will be ignored + + ! "bridge" addrmap not supported export shall refuse to process an addrmap marked as a "bridge" Only need to check top-level. Compiler will enforce that child nodes arent bridges -cpuif_resets - ! Warn/error on any signal with cpuif_reset set, that is not in the top-level - addrmap. At the very least, warn that it will be ignored - ! async data signals Only supporting async signals if they are exclusively used in resets. Anyhting else declared as "async" shall be an error I have zero interest in implementing resynchronizers +! Error if a property references a non-signal component, or property reference from + outside the export hierarchy + ! regwidth/accesswidth is sane ! accesswidth == regwidth Enforce this for now. Dont feel like supporting fancy modes yet @@ -135,9 +145,6 @@ cpuif_resets For now, probably limit to only allow the same regwidth everywhere? -! Contents of target are all internal. No external regs -! Does not contain any mem components - ! Do not allow unaligned addresses All offsets & strides shall be a multiple of the regwidth used diff --git a/doc/logbooks/template-layers/2-CPUIF b/doc/logbooks/template-layers/2-CPUIF index d0a0c98..6eb025e 100644 --- a/doc/logbooks/template-layers/2-CPUIF +++ b/doc/logbooks/template-layers/2-CPUIF @@ -21,7 +21,7 @@ Downstream Signals: - cpuif_addr Byte address - cpuif_wr_data - - cpuif_wr_bitstrb + - cpuif_wr_biten per-bit strobes some protocols may opt to tie this to all 1's - cpuif_rd_ack diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..60ba612 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1 @@ +pygments-systemrdl diff --git a/hand-coded.sv b/hand-coded.sv deleted file mode 100644 index 3a6e9da..0000000 --- a/hand-coded.sv +++ /dev/null @@ -1,223 +0,0 @@ -// Hand-coded demo. Not auto-generated - -package top_pkg; - - // top.whee[][].y - typedef struct { - logic value; - } top__wheexx__y__out_t; - - // top.whee[][] - typedef struct { - top__wheexx__y__out_t y; - } top__wheexx__out_t; - - // top.asdf[].aaa[].abc - typedef struct { - logic [14:0] value; - } top__asdfx__aaax__abc__out_t; - - // top.asdf[].aaa[].def - typedef struct { - logic [3:0] value; - } top__asdfx__aaax__def__out_t; - - // top.asdf[].aaa[] - typedef struct { - top__asdfx__aaax__abc__out_t abc; - top__asdfx__aaax__def__out_t def; - } top__asdfx__aaax__out_t; - - // top.asdf[].bbb.abc - typedef struct { - logic value; - } top__asdfx__bbb__abc__out_t; - - // top.asdf[].bbb.def - typedef struct { - logic value; - } top__asdfx__bbb__def__out_t; - - // top.asdf[].bbb - typedef struct { - top__asdfx__bbb__abc__out_t abc; - top__asdfx__bbb__def__out_t def; - } top__asdfx__bbb__out_t; - - // top.asdf[] - typedef struct { - top__asdfx__aaax__out_t aaa[4]; - top__asdfx__bbb__out_t bbb; - } top__asdfx__out_t; - - // top - typedef struct { - top__wheexx__out_t whee[2][8]; - top__asdfx__out_t asdf[20]; - } top__out_t; - -endpackage - -module top #( - // TODO: pipeline parameters - )( - input wire clk, - input wire rst, - - - apb4_intf.slave s_apb, - - output top_pkg::top__out_t hwif_out - ); - - localparam ADDR_WIDTH = 32; - localparam DATA_WIDTH = 32; - - //-------------------------------------------------------------------------- - // CPU Bus interface logic - //-------------------------------------------------------------------------- - logic cpuif_req; - logic cpuif_req_is_wr; - logic [ADDR_WIDTH-1:0] cpuif_addr; - logic [DATA_WIDTH-1:0] cpuif_wr_data; - logic [DATA_WIDTH-1:0] cpuif_wr_bitstrb; - - logic cpuif_rd_ack; - logic [DATA_WIDTH-1:0] cpuif_rd_data; - logic cpuif_rd_err; - - logic cpuif_wr_ack; - logic cpuif_wr_err; - - begin - // Request - logic is_active; - always_ff @(posedge clk) begin - if(rst) begin - is_active <= '0; - cpuif_req <= '0; - cpuif_req_is_wr <= '0; - cpuif_addr <= '0; - cpuif_wr_data <= '0; - cpuif_wr_bitstrb <= '0; - end else begin - if(~is_active) begin - if(s_apb.psel) begin - is_active <= '1; - cpuif_req <= '1; - cpuif_req_is_wr <= s_apb.pwrite; - cpuif_addr <= s_apb.paddr[ADDR_WIDTH-1:0]; - cpuif_wr_data <= s_apb.pwdata; - for(int i=0; i str: context = { "cpuif": self, - "cpuif_reset": self.cpuif_reset, - "data_width": self.data_width, - "addr_width": self.addr_width, "get_always_ff_event": get_always_ff_event, + "clog2": clog2, } template = self.exp.jj_env.get_template(self.template_path) diff --git a/peakrdl/regblock/exporter.py b/peakrdl/regblock/exporter.py index d01dc22..b03ba0e 100644 --- a/peakrdl/regblock/exporter.py +++ b/peakrdl/regblock/exporter.py @@ -3,17 +3,19 @@ from typing import Union import jinja2 as jj from systemrdl.node import AddrmapNode, RootNode +from systemrdl.walker import RDLWalker from .addr_decode import AddressDecode from .field_logic import FieldLogic from .dereferencer import Dereferencer from .readback import Readback -from .signals import InferredSignal, SignalBase +from .signals import InferredSignal, RDLSignal from .cpuif import CpuifBase from .cpuif.apb3 import APB3_Cpuif from .hwif import Hwif from .utils import get_always_ff_event +from .scan_design import DesignScanner class RegblockExporter: def __init__(self, **kwargs): @@ -29,10 +31,9 @@ class RegblockExporter: self.cpuif = None # type: CpuifBase self.address_decode = AddressDecode(self) self.field_logic = FieldLogic(self) - self.readback = Readback(self) + self.readback = None # type: Readback self.dereferencer = Dereferencer(self) self.default_resetsignal = InferredSignal("rst") - self.cpuif_reset = self.default_resetsignal if user_template_dir: @@ -67,41 +68,55 @@ class RegblockExporter: cpuif_cls = kwargs.pop("cpuif_cls", APB3_Cpuif) - hwif_cls = kwargs.pop("hwif_cls", Hwif) module_name = kwargs.pop("module_name", self.top_node.inst_name) package_name = kwargs.pop("package_name", module_name + "_pkg") - module_file_path = os.path.join(output_dir, module_name + ".sv") - package_file_path = os.path.join(output_dir, package_name + ".sv") + + # Pipelining options + retime_read_response = kwargs.pop("retime_read_response", True) + retime_read_fanin = kwargs.pop("retime_read_fanin", False) # Check for stray kwargs if kwargs: raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) + # Scan the design for any unsupported features + # Also collect pre-export information + scanner = DesignScanner(self) + RDLWalker().walk(self.top_node, scanner) + if scanner.msg.had_error: + scanner.msg.fatal( + "Unable to export due to previous errors" + ) + raise ValueError - # TODO: Scan design... - - # TODO: derive this from somewhere - self.cpuif_reset = self.default_resetsignal - reset_signals = set([self.cpuif_reset, self.default_resetsignal]) + cpuif_reset_tmp = self.top_node.cpuif_reset + if cpuif_reset_tmp: + cpuif_reset = RDLSignal(cpuif_reset_tmp) + else: + cpuif_reset = self.default_resetsignal + reset_signals = set([cpuif_reset, self.default_resetsignal]) self.cpuif = cpuif_cls( self, - cpuif_reset=self.cpuif_reset, # TODO: - data_width=32, # TODO: derive from the regwidth used by regs - addr_width=32 # TODO: + cpuif_reset=cpuif_reset, + data_width=scanner.cpuif_data_width, + addr_width=self.top_node.size.bit_length() ) - self.hwif = hwif_cls( + self.hwif = Hwif( self, package_name=package_name, ) + self.readback = Readback( + self, + retime_read_fanin + ) + # Build Jinja template context context = { "module_name": module_name, - "data_width": 32, # TODO: - "addr_width": 32, # TODO: "reset_signals": reset_signals, "user_signals": [], # TODO: "interrupts": [], # TODO: @@ -110,13 +125,17 @@ class RegblockExporter: "address_decode": self.address_decode, "field_logic": self.field_logic, "readback": self.readback, + "get_always_ff_event": get_always_ff_event, + "retime_read_response": retime_read_response, } # Write out design + package_file_path = os.path.join(output_dir, package_name + ".sv") template = self.jj_env.get_template("package_tmpl.sv") stream = template.stream(context) stream.dump(package_file_path) + module_file_path = os.path.join(output_dir, module_name + ".sv") template = self.jj_env.get_template("module_tmpl.sv") stream = template.stream(context) stream.dump(module_file_path) diff --git a/peakrdl/regblock/module_tmpl.sv b/peakrdl/regblock/module_tmpl.sv index 64f2cdc..4df5e4e 100644 --- a/peakrdl/regblock/module_tmpl.sv +++ b/peakrdl/regblock/module_tmpl.sv @@ -19,20 +19,17 @@ module {{module_name}} ( {{hwif.port_declaration|indent(8)}} ); - localparam ADDR_WIDTH = {{addr_width}}; - localparam DATA_WIDTH = {{data_width}}; - //-------------------------------------------------------------------------- // CPU Bus interface logic //-------------------------------------------------------------------------- logic cpuif_req; logic cpuif_req_is_wr; - logic [ADDR_WIDTH-1:0] cpuif_addr; - logic [DATA_WIDTH-1:0] cpuif_wr_data; - logic [DATA_WIDTH-1:0] cpuif_wr_bitstrb; + 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_rd_ack; - logic [DATA_WIDTH-1:0] cpuif_rd_data; + logic [{{cpuif.data_width-1}}:0] cpuif_rd_data; logic cpuif_rd_err; logic cpuif_wr_ack; @@ -47,8 +44,8 @@ module {{module_name}} ( decoded_reg_strb_t decoded_reg_strb; logic decoded_req; logic decoded_req_is_wr; - logic [DATA_WIDTH-1:0] decoded_wr_data; - logic [DATA_WIDTH-1:0] decoded_wr_bitstrb; + 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)}} @@ -62,7 +59,7 @@ module {{module_name}} ( assign decoded_req = cpuif_req; assign decoded_req_is_wr = cpuif_req_is_wr; assign decoded_wr_data = cpuif_wr_data; - assign decoded_wr_bitstrb = cpuif_wr_bitstrb; + assign decoded_wr_biten = cpuif_wr_biten; //-------------------------------------------------------------------------- // Field logic @@ -76,6 +73,27 @@ module {{module_name}} ( //-------------------------------------------------------------------------- // Readback //-------------------------------------------------------------------------- + logic readback_err; + logic readback_done; + logic [{{cpuif.data_width-1}}:0] readback_data; {{readback.get_implementation()|indent}} +{% if retime_read_response %} + always_ff {{get_always_ff_event(cpuif.reset)}} begin + if({{cpuif.reset.activehigh_identifier}}) begin + cpuif_rd_ack <= '0; + cpuif_rd_data <= '0; + cpuif_rd_err <= '0; + end else begin + cpuif_rd_ack <= readback_done; + cpuif_rd_data <= readback_data; + cpuif_rd_err <= readback_err; + end + end +{% else %} + assign cpuif_rd_ack = readback_done; + assign cpuif_rd_data = readback_data; + assign cpuif_rd_err = readback_err; +{% endif %} + endmodule diff --git a/peakrdl/regblock/readback/__init__.py b/peakrdl/regblock/readback/__init__.py index fa09153..46d8b98 100644 --- a/peakrdl/regblock/readback/__init__.py +++ b/peakrdl/regblock/readback/__init__.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING +import math from .generators import ReadbackAssignmentGenerator from ..utils import get_always_ff_event @@ -8,8 +9,9 @@ if TYPE_CHECKING: from systemrdl.node import AddrmapNode class Readback: - def __init__(self, exp:'RegblockExporter'): + def __init__(self, exp:'RegblockExporter', do_fanin_stage: bool): self.exp = exp + self.do_fanin_stage = do_fanin_stage @property def top_node(self) -> 'AddrmapNode': @@ -18,13 +20,49 @@ class Readback: def get_implementation(self) -> str: gen = ReadbackAssignmentGenerator(self.exp) array_assignments = gen.get_content(self.top_node) + array_size = gen.current_offset + + # Enabling the fanin stage doesnt make sense if readback fanin is + # small. This also avoids pesky corner cases + if array_size < 4: + self.do_fanin_stage = False context = { "array_assignments" : array_assignments, - "array_size" : gen.current_offset, + "array_size" : array_size, "get_always_ff_event": get_always_ff_event, - "cpuif_reset": self.exp.cpuif_reset, + "cpuif": self.exp.cpuif, + "do_fanin_stage": self.do_fanin_stage, } + + if self.do_fanin_stage: + # If adding a fanin pipeline stage, goal is to try to + # split the fanin path in the middle so that fanin into the stage + # and the following are roughly balanced. + fanin_target = math.sqrt(array_size) + + # Size of fanin group to consume per fanin element + fanin_stride = math.floor(fanin_target) + + # Number of array elements to reduce to. + # Round up to an extra element in case there is some residual + fanin_array_size = math.ceil(array_size / fanin_stride) + + # leftovers are handled in an extra array element + fanin_residual_stride = array_size % fanin_stride + + if fanin_residual_stride != 0: + # If there is a partial fanin element, reduce the number of + # loops performed in the bulk fanin stage + fanin_loop_iter = fanin_array_size - 1 + else: + fanin_loop_iter = fanin_array_size + + context['fanin_stride'] = fanin_stride + context['fanin_array_size'] = fanin_array_size + context['fanin_residual_stride'] = fanin_residual_stride + context['fanin_loop_iter'] = fanin_loop_iter + template = self.exp.jj_env.get_template( "readback/templates/readback.sv" ) diff --git a/peakrdl/regblock/readback/templates/readback.sv b/peakrdl/regblock/readback/templates/readback.sv index def514b..2e8a770 100644 --- a/peakrdl/regblock/readback/templates/readback.sv +++ b/peakrdl/regblock/readback/templates/readback.sv @@ -1,45 +1,68 @@ {% if array_assignments is not none %} -logic readback_err; -logic readback_done; -logic [DATA_WIDTH-1:0] readback_data; -logic [DATA_WIDTH-1:0] readback_array[{{array_size}}]; - +// Assign readback values to a flattened array +logic [{{cpuif.data_width-1}}:0] readback_array[{{array_size}}]; {{array_assignments}} +{% if do_fanin_stage %} +// fanin stage +logic [31:0] readback_array_c[{{fanin_array_size}}]; +for(genvar g=0; g<{{fanin_loop_iter}}; g++) begin + always_comb begin + automatic logic [31:0] readback_data_var; + readback_data_var = '0; + for(int i=g*{{fanin_stride}}; i<((g+1)*{{fanin_stride}}); i++) readback_data_var |= readback_array[i]; + readback_array_c[g] = readback_data_var; + end +end +{%- if fanin_residual_stride == 1 %} +assign readback_array_c[{{fanin_array_size-1}}] = readback_array[{{array_size-1}}]; +{%- elif fanin_residual_stride > 1 %} always_comb begin - automatic logic [DATA_WIDTH-1:0] readback_data_var; + automatic logic [31:0] readback_data_var; + readback_data_var = '0; + for(int i={{(fanin_array_size-1) * fanin_stride}}; i<{{array_size-1}}; i++) readback_data_var |= readback_array[i]; + readback_array_c[{{fanin_array_size-1}}] = readback_data_var; +end +{%- endif %} + +logic [31:0] readback_array_r[{{fanin_array_size}}]; +logic readback_done_r; +always_ff @(posedge clk) begin + if(rst) begin + for(int i=0; i<{{fanin_array_size}}; i++) readback_array_r[i] <= '0; + readback_done_r <= '0; + end else begin + readback_array_r <= readback_array_c; + readback_done_r <= decoded_req & ~decoded_req_is_wr; + end +end + +// Reduce the array +always_comb begin + automatic logic [31:0] readback_data_var; readback_done = decoded_req & ~decoded_req_is_wr; readback_err = '0; - readback_data_var = '0; - for(int i=0; i<{{array_size}}; i++) begin - readback_data_var |= readback_array[i]; - end + for(int i=0; i<{{fanin_array_size}}; i++) readback_data_var |= readback_array_r[i]; readback_data = readback_data_var; end -always_ff {{get_always_ff_event(cpuif_reset)}} begin - if({{cpuif_reset.activehigh_identifier}}) begin - cpuif_rd_ack <= '0; - cpuif_rd_data <= '0; - cpuif_rd_err <= '0; - end else begin - cpuif_rd_ack <= readback_done; - cpuif_rd_data <= readback_data; - cpuif_rd_err <= readback_err; - end +{%- else %} +// Reduce the array +always_comb begin + automatic logic [{{cpuif.data_width-1}}:0] readback_data_var; + readback_done = decoded_req & ~decoded_req_is_wr; + readback_err = '0; + readback_data_var = '0; + for(int i=0; i<{{array_size}}; i++) readback_data_var |= readback_array[i]; + readback_data = readback_data_var; end +{%- endif %} {%- else %} -always_ff {{get_always_ff_event(cpuif_reset)}} begin - if({{cpuif_reset.activehigh_identifier}}) begin - cpuif_rd_ack <= '0; - end else begin - cpuif_rd_ack <= decoded_req & ~decoded_req_is_wr; - end -end -assign cpuif_rd_data = '0; -assign cpuif_rd_err = '0; +assign readback_done = decoded_req & ~decoded_req_is_wr; +assign readback_data = '0; +assign readback_err = '0; {% endif %} diff --git a/peakrdl/regblock/scan_design.py b/peakrdl/regblock/scan_design.py index 8afe5be..cde9135 100644 --- a/peakrdl/regblock/scan_design.py +++ b/peakrdl/regblock/scan_design.py @@ -1,14 +1,53 @@ +from typing import TYPE_CHECKING -""" -- Signal References - Collect any references to signals that lie outside of the hierarchy - These will be added as top-level signals -- top-level interrupts +from systemrdl.walker import RDLListener +from systemrdl.node import AddrmapNode + +if TYPE_CHECKING: + from systemrdl.node import Node, RegNode, SignalNode, MemNode + from .exporter import RegblockExporter -Validate: - - Error if a property references a non-signal component, or property reference from - outside the export hierarchy - - No Mem components allowed - - Uniform regwidth, accesswidth, etc. -""" +class DesignScanner(RDLListener): + """ + Scans through the register model and validates that any unsupported features + are not present. + + Also collects any information that is required prior to the start of the export process. + """ + def __init__(self, exp:'RegblockExporter'): + self.exp = exp + self.cpuif_data_width = 0 + self.msg = exp.top_node.env.msg + + def enter_Reg(self, node: 'RegNode') -> None: + # The CPUIF's bus width is sized according to the largest register in the design + self.cpuif_data_width = max(self.cpuif_data_width, node.get_property("regwidth")) + + # TODO: Collect any references to signals that lie outside of the hierarchy + # These will be added as top-level signals + + def enter_Component(self, node: 'Node') -> None: + if not isinstance(node, AddrmapNode) and node.external: + self.msg.error( + "Exporter does not support external components", + node.inst.inst_src_ref + ) + + def enter_Signal(self, node: 'SignalNode') -> None: + # If encountering a CPUIF reset that is nested within the register model, + # warn that it will be ignored. + # Only cpuif resets in the top-level node or above will be honored + if node.get_property('cpuif_reset') and (node.parent != self.exp.top_node): + self.msg.warning( + "Only cpuif_reset signals that are instantiated in the top-level " + + "addrmap or above will be honored. Any cpuif_reset signals nested " + + "within children of the addrmap being exported will be ignored.", + node.inst.inst_src_ref + ) + + def enter_Mem(self, node: 'MemNode') -> None: + self.msg.error( + "Cannot export a register block that contains a memory", + node.inst.inst_src_ref + ) diff --git a/peakrdl/regblock/utils.py b/peakrdl/regblock/utils.py index cbc4bfa..45ade86 100644 --- a/peakrdl/regblock/utils.py +++ b/peakrdl/regblock/utils.py @@ -30,3 +30,6 @@ def get_always_ff_event(resetsignal: 'Optional[SignalBase]') -> str: elif resetsignal.is_async and not resetsignal.is_activehigh: return f"@(posedge clk or negedge {resetsignal.identifier})" return "@(posedge clk)" + +def clog2(n: int) -> int: + return n.bit_length() - 1 diff --git a/test/run.sh b/test/run.sh index f142109..af3f8df 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,7 +1,8 @@ #!/bin/bash set -e + ../export.py test_regblock.rdl -vlog -sv -suppress 2720 -quiet -f src.f +vlog -sv -f vlog_args.f -f src.f vsim -c -quiet tb -do "log -r /*; run -all; exit;" diff --git a/test/test_regblock.rdl b/test/test_regblock.rdl index 72ff1db..7287059 100644 --- a/test/test_regblock.rdl +++ b/test/test_regblock.rdl @@ -2,11 +2,11 @@ addrmap test_regblock { reg my_reg { field { sw=rw; hw=r; anded;} a[8] = 0x10; - field { sw=rw; hw=r; ored;} b[8] = 0x20; - field { sw=rw; hw=r; swmod;} c[8] = 0x30; + //field { sw=rw; hw=r; ored;} b[8] = 0x20; + //field { sw=rw; hw=r; swmod;} c[8] = 0x30; }; - my_reg r0 @0x000; - my_reg r1 @0x100; - my_reg r2 @0x200; + //my_reg r0 @0x000; + //my_reg r1 @0x100; + my_reg r2[112] @0x200 += 8; }; diff --git a/test/test_regblock.sv b/test/test_regblock.sv index 7344d6a..08289c9 100644 --- a/test/test_regblock.sv +++ b/test/test_regblock.sv @@ -8,20 +8,17 @@ module test_regblock ( output test_regblock_pkg::test_regblock__out_t hwif_out ); - localparam ADDR_WIDTH = 32; - localparam DATA_WIDTH = 32; - //-------------------------------------------------------------------------- // CPU Bus interface logic //-------------------------------------------------------------------------- logic cpuif_req; logic cpuif_req_is_wr; - logic [ADDR_WIDTH-1:0] cpuif_addr; - logic [DATA_WIDTH-1:0] cpuif_wr_data; - logic [DATA_WIDTH-1:0] cpuif_wr_bitstrb; + logic [10:0] cpuif_addr; + logic [31:0] cpuif_wr_data; + logic [31:0] cpuif_wr_biten; logic cpuif_rd_ack; - logic [DATA_WIDTH-1:0] cpuif_rd_data; + logic [31:0] cpuif_rd_data; logic cpuif_rd_err; logic cpuif_wr_ack; @@ -43,7 +40,7 @@ module test_regblock ( is_active <= '1; cpuif_req <= '1; cpuif_req_is_wr <= s_apb.PWRITE; - cpuif_addr <= s_apb.PADDR[ADDR_WIDTH-1:0]; + cpuif_addr <= {s_apb.PADDR[10:2], 2'b0}; cpuif_wr_data <= s_apb.PWDATA; end end else begin @@ -54,7 +51,7 @@ module test_regblock ( end end end - assign cpuif_wr_bitstrb = '0; + assign cpuif_wr_biten = '1; // Response assign s_apb.PREADY = cpuif_rd_ack | cpuif_wr_ack; @@ -66,20 +63,18 @@ module test_regblock ( // Address Decode //-------------------------------------------------------------------------- typedef struct { - logic r0; - logic r1; - logic r2; + logic r2[112]; } decoded_reg_strb_t; decoded_reg_strb_t decoded_reg_strb; logic decoded_req; logic decoded_req_is_wr; - logic [DATA_WIDTH-1:0] decoded_wr_data; - logic [DATA_WIDTH-1:0] decoded_wr_bitstrb; + logic [31:0] decoded_wr_data; + logic [31:0] decoded_wr_biten; always_comb begin - decoded_reg_strb.r0 = cpuif_req & (cpuif_addr == 'h0); - decoded_reg_strb.r1 = cpuif_req & (cpuif_addr == 'h100); - decoded_reg_strb.r2 = cpuif_req & (cpuif_addr == 'h200); + for(int i0=0; i0<112; i0++) begin + decoded_reg_strb.r2[i0] = cpuif_req & (cpuif_addr == 'h200 + i0*'h8); + end end // Writes are always granted with no error response @@ -90,7 +85,7 @@ module test_regblock ( assign decoded_req = cpuif_req; assign decoded_req_is_wr = cpuif_req_is_wr; assign decoded_wr_data = cpuif_wr_data; - assign decoded_wr_bitstrb = cpuif_wr_bitstrb; + assign decoded_wr_biten = cpuif_wr_biten; //-------------------------------------------------------------------------- // Field logic @@ -101,262 +96,64 @@ module test_regblock ( logic [7:0] next; logic load_next; } a; - struct { - logic [7:0] next; - logic load_next; - } b; - struct { - logic [7:0] next; - logic load_next; - } c; - } r0; - struct { - struct { - logic [7:0] next; - logic load_next; - } a; - struct { - logic [7:0] next; - logic load_next; - } b; - struct { - logic [7:0] next; - logic load_next; - } c; - } r1; - struct { - struct { - logic [7:0] next; - logic load_next; - } a; - struct { - logic [7:0] next; - logic load_next; - } b; - struct { - logic [7:0] next; - logic load_next; - } c; - } r2; + } r2[112]; } field_combo_t; field_combo_t field_combo; typedef struct { struct { logic [7:0] a; - logic [7:0] b; - logic [7:0] c; - } r0; - struct { - logic [7:0] a; - logic [7:0] b; - logic [7:0] c; - } r1; - struct { - logic [7:0] a; - logic [7:0] b; - logic [7:0] c; - } r2; + } r2[112]; } field_storage_t; field_storage_t field_storage; - // Field: test_regblock.r0.a - always_comb begin - field_combo.r0.a.next = field_storage.r0.a; - field_combo.r0.a.load_next = '0; - if(decoded_reg_strb.r0 && decoded_req_is_wr) begin // SW write - field_combo.r0.a.next = decoded_wr_data[7:0]; - field_combo.r0.a.load_next = '1; + for(genvar i0=0; i0<112; i0++) begin + // Field: test_regblock.r2[].a + always_comb begin + field_combo.r2[i0].a.next = field_storage.r2[i0].a; + field_combo.r2[i0].a.load_next = '0; + if(decoded_reg_strb.r2[i0] && decoded_req_is_wr) begin // SW write + field_combo.r2[i0].a.next = decoded_wr_data[7:0]; + field_combo.r2[i0].a.load_next = '1; + end end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r0.a <= 'h10; - end else if(field_combo.r0.a.load_next) begin - field_storage.r0.a <= field_combo.r0.a.next; + always_ff @(posedge clk) begin + if(rst) begin + field_storage.r2[i0].a <= 'h10; + end else if(field_combo.r2[i0].a.load_next) begin + field_storage.r2[i0].a <= field_combo.r2[i0].a.next; + end end + assign hwif_out.r2[i0].a.value = field_storage.r2[i0].a; + assign hwif_out.r2[i0].a.anded = &(field_storage.r2[i0].a); end - assign hwif_out.r0.a.value = field_storage.r0.a; - assign hwif_out.r0.a.anded = &(field_storage.r0.a); - // Field: test_regblock.r0.b - always_comb begin - field_combo.r0.b.next = field_storage.r0.b; - field_combo.r0.b.load_next = '0; - if(decoded_reg_strb.r0 && decoded_req_is_wr) begin // SW write - field_combo.r0.b.next = decoded_wr_data[15:8]; - field_combo.r0.b.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r0.b <= 'h20; - end else if(field_combo.r0.b.load_next) begin - field_storage.r0.b <= field_combo.r0.b.next; - end - end - assign hwif_out.r0.b.value = field_storage.r0.b; - assign hwif_out.r0.b.ored = |(field_storage.r0.b); - // Field: test_regblock.r0.c - always_comb begin - field_combo.r0.c.next = field_storage.r0.c; - field_combo.r0.c.load_next = '0; - if(decoded_reg_strb.r0 && decoded_req_is_wr) begin // SW write - field_combo.r0.c.next = decoded_wr_data[23:16]; - field_combo.r0.c.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r0.c <= 'h30; - end else if(field_combo.r0.c.load_next) begin - field_storage.r0.c <= field_combo.r0.c.next; - end - end - assign hwif_out.r0.c.value = field_storage.r0.c; - assign hwif_out.r0.c.swmod = decoded_reg_strb.r0 && decoded_req_is_wr; - // Field: test_regblock.r1.a - always_comb begin - field_combo.r1.a.next = field_storage.r1.a; - field_combo.r1.a.load_next = '0; - if(decoded_reg_strb.r1 && decoded_req_is_wr) begin // SW write - field_combo.r1.a.next = decoded_wr_data[7:0]; - field_combo.r1.a.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r1.a <= 'h10; - end else if(field_combo.r1.a.load_next) begin - field_storage.r1.a <= field_combo.r1.a.next; - end - end - assign hwif_out.r1.a.value = field_storage.r1.a; - assign hwif_out.r1.a.anded = &(field_storage.r1.a); - // Field: test_regblock.r1.b - always_comb begin - field_combo.r1.b.next = field_storage.r1.b; - field_combo.r1.b.load_next = '0; - if(decoded_reg_strb.r1 && decoded_req_is_wr) begin // SW write - field_combo.r1.b.next = decoded_wr_data[15:8]; - field_combo.r1.b.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r1.b <= 'h20; - end else if(field_combo.r1.b.load_next) begin - field_storage.r1.b <= field_combo.r1.b.next; - end - end - assign hwif_out.r1.b.value = field_storage.r1.b; - assign hwif_out.r1.b.ored = |(field_storage.r1.b); - // Field: test_regblock.r1.c - always_comb begin - field_combo.r1.c.next = field_storage.r1.c; - field_combo.r1.c.load_next = '0; - if(decoded_reg_strb.r1 && decoded_req_is_wr) begin // SW write - field_combo.r1.c.next = decoded_wr_data[23:16]; - field_combo.r1.c.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r1.c <= 'h30; - end else if(field_combo.r1.c.load_next) begin - field_storage.r1.c <= field_combo.r1.c.next; - end - end - assign hwif_out.r1.c.value = field_storage.r1.c; - assign hwif_out.r1.c.swmod = decoded_reg_strb.r1 && decoded_req_is_wr; - // Field: test_regblock.r2.a - always_comb begin - field_combo.r2.a.next = field_storage.r2.a; - field_combo.r2.a.load_next = '0; - if(decoded_reg_strb.r2 && decoded_req_is_wr) begin // SW write - field_combo.r2.a.next = decoded_wr_data[7:0]; - field_combo.r2.a.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r2.a <= 'h10; - end else if(field_combo.r2.a.load_next) begin - field_storage.r2.a <= field_combo.r2.a.next; - end - end - assign hwif_out.r2.a.value = field_storage.r2.a; - assign hwif_out.r2.a.anded = &(field_storage.r2.a); - // Field: test_regblock.r2.b - always_comb begin - field_combo.r2.b.next = field_storage.r2.b; - field_combo.r2.b.load_next = '0; - if(decoded_reg_strb.r2 && decoded_req_is_wr) begin // SW write - field_combo.r2.b.next = decoded_wr_data[15:8]; - field_combo.r2.b.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r2.b <= 'h20; - end else if(field_combo.r2.b.load_next) begin - field_storage.r2.b <= field_combo.r2.b.next; - end - end - assign hwif_out.r2.b.value = field_storage.r2.b; - assign hwif_out.r2.b.ored = |(field_storage.r2.b); - // Field: test_regblock.r2.c - always_comb begin - field_combo.r2.c.next = field_storage.r2.c; - field_combo.r2.c.load_next = '0; - if(decoded_reg_strb.r2 && decoded_req_is_wr) begin // SW write - field_combo.r2.c.next = decoded_wr_data[23:16]; - field_combo.r2.c.load_next = '1; - end - end - always_ff @(posedge clk) begin - if(rst) begin - field_storage.r2.c <= 'h30; - end else if(field_combo.r2.c.load_next) begin - field_storage.r2.c <= field_combo.r2.c.next; - end - end - assign hwif_out.r2.c.value = field_storage.r2.c; - assign hwif_out.r2.c.swmod = decoded_reg_strb.r2 && decoded_req_is_wr; //-------------------------------------------------------------------------- // Readback //-------------------------------------------------------------------------- - logic readback_err; logic readback_done; - logic [DATA_WIDTH-1:0] readback_data; - logic [DATA_WIDTH-1:0] readback_array[3]; + logic [31:0] readback_data; + + // Assign readback values to a flattened array + logic [31:0] readback_array[112]; + for(genvar i0=0; i0<112; i0++) begin + assign readback_array[i0*1 + 0][7:0] = (decoded_reg_strb.r2[i0] && !decoded_req_is_wr) ? field_storage.r2[i0].a : '0; + assign readback_array[i0*1 + 0][31:8] = '0; + end - assign readback_array[0][7:0] = (decoded_reg_strb.r0 && !decoded_req_is_wr) ? field_storage.r0.a : '0; - assign readback_array[0][15:8] = (decoded_reg_strb.r0 && !decoded_req_is_wr) ? field_storage.r0.b : '0; - assign readback_array[0][23:16] = (decoded_reg_strb.r0 && !decoded_req_is_wr) ? field_storage.r0.c : '0; - assign readback_array[0][31:24] = '0; - assign readback_array[1][7:0] = (decoded_reg_strb.r1 && !decoded_req_is_wr) ? field_storage.r1.a : '0; - assign readback_array[1][15:8] = (decoded_reg_strb.r1 && !decoded_req_is_wr) ? field_storage.r1.b : '0; - assign readback_array[1][23:16] = (decoded_reg_strb.r1 && !decoded_req_is_wr) ? field_storage.r1.c : '0; - assign readback_array[1][31:24] = '0; - assign readback_array[2][7:0] = (decoded_reg_strb.r2 && !decoded_req_is_wr) ? field_storage.r2.a : '0; - assign readback_array[2][15:8] = (decoded_reg_strb.r2 && !decoded_req_is_wr) ? field_storage.r2.b : '0; - assign readback_array[2][23:16] = (decoded_reg_strb.r2 && !decoded_req_is_wr) ? field_storage.r2.c : '0; - assign readback_array[2][31:24] = '0; + // Reduce the array always_comb begin - automatic logic [DATA_WIDTH-1:0] readback_data_var; + automatic logic [31:0] readback_data_var; readback_done = decoded_req & ~decoded_req_is_wr; readback_err = '0; - readback_data_var = '0; - for(int i=0; i<3; i++) begin - readback_data_var |= readback_array[i]; - end + for(int i=0; i<112; i++) readback_data_var |= readback_array[i]; readback_data = readback_data_var; end + always_ff @(posedge clk) begin if(rst) begin cpuif_rd_ack <= '0; @@ -369,4 +166,5 @@ module test_regblock ( end end + endmodule \ No newline at end of file diff --git a/test/test_regblock_pkg.sv b/test/test_regblock_pkg.sv index 8e41268..b0fed1b 100644 --- a/test/test_regblock_pkg.sv +++ b/test/test_regblock_pkg.sv @@ -1,85 +1,19 @@ // TODO: Add a banner package test_regblock_pkg; - // test_regblock.r0.a + // test_regblock.r2[].a typedef struct { logic [7:0] value; logic anded; - } test_regblock__r0__a__out_t; + } test_regblock__r2x__a__out_t; - // test_regblock.r0.b + // test_regblock.r2[] typedef struct { - logic [7:0] value; - logic ored; - } test_regblock__r0__b__out_t; - - // test_regblock.r0.c - typedef struct { - logic [7:0] value; - logic swmod; - } test_regblock__r0__c__out_t; - - // test_regblock.r0 - typedef struct { - test_regblock__r0__a__out_t a; - test_regblock__r0__b__out_t b; - test_regblock__r0__c__out_t c; - } test_regblock__r0__out_t; - - // test_regblock.r1.a - typedef struct { - logic [7:0] value; - logic anded; - } test_regblock__r1__a__out_t; - - // test_regblock.r1.b - typedef struct { - logic [7:0] value; - logic ored; - } test_regblock__r1__b__out_t; - - // test_regblock.r1.c - typedef struct { - logic [7:0] value; - logic swmod; - } test_regblock__r1__c__out_t; - - // test_regblock.r1 - typedef struct { - test_regblock__r1__a__out_t a; - test_regblock__r1__b__out_t b; - test_regblock__r1__c__out_t c; - } test_regblock__r1__out_t; - - // test_regblock.r2.a - typedef struct { - logic [7:0] value; - logic anded; - } test_regblock__r2__a__out_t; - - // test_regblock.r2.b - typedef struct { - logic [7:0] value; - logic ored; - } test_regblock__r2__b__out_t; - - // test_regblock.r2.c - typedef struct { - logic [7:0] value; - logic swmod; - } test_regblock__r2__c__out_t; - - // test_regblock.r2 - typedef struct { - test_regblock__r2__a__out_t a; - test_regblock__r2__b__out_t b; - test_regblock__r2__c__out_t c; - } test_regblock__r2__out_t; + test_regblock__r2x__a__out_t a; + } test_regblock__r2x__out_t; // test_regblock typedef struct { - test_regblock__r0__out_t r0; - test_regblock__r1__out_t r1; - test_regblock__r2__out_t r2; + test_regblock__r2x__out_t r2[112]; } test_regblock__out_t; endpackage \ No newline at end of file diff --git a/test/vlog_args.f b/test/vlog_args.f new file mode 100644 index 0000000..12ea952 --- /dev/null +++ b/test/vlog_args.f @@ -0,0 +1,9 @@ +-quiet + +# Free version of ModelSim errors if generate statements are not used. +# These have been made optional long ago. Modern versions of SystemVerilog do +# not require them. +-suppress 2720 + +# Ignore warning about vopt-time checking of always_comb/always_latch +-suppress 2583 diff --git a/test/wave.do b/test/wave.do index 2f5091f..28083b2 100644 --- a/test/wave.do +++ b/test/wave.do @@ -15,7 +15,7 @@ add wave -noupdate /tb/dut/cpuif_req add wave -noupdate /tb/dut/cpuif_req_is_wr add wave -noupdate /tb/dut/cpuif_addr add wave -noupdate /tb/dut/cpuif_wr_data -add wave -noupdate /tb/dut/cpuif_wr_bitstrb +add wave -noupdate /tb/dut/cpuif_wr_biten add wave -noupdate /tb/dut/cpuif_rd_ack add wave -noupdate /tb/dut/cpuif_rd_data add wave -noupdate /tb/dut/cpuif_rd_err