readback!

This commit is contained in:
Alex Mykyta
2021-11-19 23:34:55 -08:00
parent 249fc2df7c
commit d3c876a491
20 changed files with 316 additions and 651 deletions

View File

@@ -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']
html_static_path = ['_static']

View File

@@ -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

View File

@@ -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

View File

@@ -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

1
doc/requirements.txt Normal file
View File

@@ -0,0 +1 @@
pygments-systemrdl

View File

@@ -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<DATA_WIDTH/8; i++) begin
cpuif_wr_bitstrb[i*8 +: 8] <= {8{s_apb.pstrb[i]}};
end
end
end else begin
cpuif_req <= '0;
if(cpuif_rd_ack || cpuif_wr_ack) begin
is_active <= '0;
end
end
end
end
// Response
assign s_apb.pready = cpuif_rd_ack | cpuif_wr_ack;
assign s_apb.prdata = cpuif_rd_data;
assign s_apb.pslverr = cpuif_rd_err | cpuif_wr_err;
end
//--------------------------------------------------------------------------
// Address Decode
//--------------------------------------------------------------------------
typedef struct {
logic whee[2][8];
struct {
logic aaa[4];
logic bbb;
} asdf[20];
} access_strb_t;
access_strb_t access_strb;
always_comb begin
for(int i0=0; i0<2; i0++) begin
for(int i1=0; i1<8; i1++) begin
access_strb.whee[i0][i1] = cpuif_req & (cpuif_addr == 'h0 + i0*'h20 + i1*'h4);
end
end
for(int i0=0; i0<20; i0++) begin
for(int i1=0; i1<4; i1++) begin
access_strb.asdf[i0].aaa[i1] = cpuif_req & (cpuif_addr == 'h40 + i0*'h14 + i1*'h4);
end
access_strb.asdf[i0].bbb = cpuif_req & (cpuif_addr == 'h50 + i0*'h14);
end
end
// Writes are always posted with no error response
assign cpuif_wr_ack = cpuif_req & cpuif_req_is_wr;
assign cpuif_wr_err = '0;
//--------------------------------------------------------------------------
// Field logic
//--------------------------------------------------------------------------
typedef struct {
struct {
logic y;
} whee[2][8];
struct {
struct {
logic [14:0] abc;
logic [3:0] def;
} aaa[4];
struct {
logic abc;
logic def;
} bbb;
} asdf[20];
} field_storage_t;
field_storage_t field_storage;
// TODO: Field next-state logic, and output port signal assignment (aka output mapping layer)
TODO:
//--------------------------------------------------------------------------
// Readback mux
//--------------------------------------------------------------------------
logic readback_err;
logic [DATA_WIDTH-1:0] readback_data;
always_comb begin
readback_err = '0;
readback_data = '0;
if(cpuif_req & ~cpuif_req_is_wr) begin
readback_err = '1;
for(int i0=0; i0<2; i0++) begin
for(int i1=0; i1<8; i1++) begin
if(cpuif_addr == 'h0 + i0*'h20 + i1*'h4) begin
readback_err = '0;
readback_data[0:0] = field_storage.whee[i0][i1].y;
end
end
end
for(int i0=0; i0<20; i0++) begin
for(int i1=0; i1<4; i1++) begin
if(cpuif_addr == 'h40 + i0*'h14 + i1*'h4) begin
readback_err = '0;
readback_data[16:2] = field_storage.asdf[i0].aaa[i1].abc;
readback_data[4:4] = field_storage.asdf[i0].aaa[i1].def;
end
end
if(cpuif_addr == 'h50 + i0*'h14) begin
readback_err = '0;
readback_data[0:0] = field_storage.asdf[i0].bbb.abc;
readback_data[1:1] = field_storage.asdf[i0].bbb.def;
end
end
end
end
assign cpuif_rd_ack = cpuif_req & ~cpuif_req_is_wr;
assign cpuif_rd_data = readback_data;
assign cpuif_rd_err = readback_err;
endmodule

View File

@@ -3,8 +3,8 @@
{% block body %}
// Request
logic is_active;
always_ff {{get_always_ff_event(cpuif_reset)}} begin
if({{cpuif_reset.activehigh_identifier}}) begin
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{cpuif.reset.activehigh_identifier}}) begin
is_active <= '0;
cpuif_req <= '0;
cpuif_req_is_wr <= '0;
@@ -16,7 +16,11 @@ always_ff {{get_always_ff_event(cpuif_reset)}} begin
is_active <= '1;
cpuif_req <= '1;
cpuif_req_is_wr <= {{cpuif.signal("pwrite")}};
cpuif_addr <= {{cpuif.signal("paddr")}}[ADDR_WIDTH-1:0];
{%- if cpuif.data_width == 8 %}
cpuif_addr <= {{cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:0];
{%- else %}
cpuif_addr <= { {{-cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width//8)}}], {{clog2(cpuif.data_width//8)}}'b0};
{%- endif %}
cpuif_wr_data <= {{cpuif.signal("pwdata")}};
end
end else begin
@@ -27,7 +31,7 @@ always_ff {{get_always_ff_event(cpuif_reset)}} begin
end
end
end
assign cpuif_wr_bitstrb = '0;
assign cpuif_wr_biten = '1;
// Response
assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack;

View File

@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
from ..utils import get_always_ff_event
from ..utils import get_always_ff_event, clog2
if TYPE_CHECKING:
from ..exporter import RegblockExporter
@@ -11,7 +11,7 @@ class CpuifBase:
def __init__(self, exp:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
self.exp = exp
self.cpuif_reset = cpuif_reset
self.reset = cpuif_reset
self.data_width = data_width
self.addr_width = addr_width
@@ -22,10 +22,8 @@ class CpuifBase:
def get_implementation(self) -> 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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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 %}

View File

@@ -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
)

View File

@@ -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

View File

@@ -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;"

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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

9
test/vlog_args.f Normal file
View File

@@ -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

View File

@@ -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