readback!
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
1
doc/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
pygments-systemrdl
|
||||
223
hand-coded.sv
223
hand-coded.sv
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;"
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
9
test/vlog_args.f
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user