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,7 +45,7 @@ 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,

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 [DATA_WIDTH-1:0] readback_data_var;
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 [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
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
class DesignScanner(RDLListener):
"""
- Signal References
Collect any references to signals that lie outside of the hierarchy
These will be added as top-level signals
- top-level interrupts
Scans through the register model and validates that any unsupported features
are not present.
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.
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
for(genvar i0=0; i0<112; i0++) begin
// Field: test_regblock.r2[].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;
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
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;
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.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;
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
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_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;
// 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
// 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