409 lines
12 KiB
Smarty
409 lines
12 KiB
Smarty
// Copyright lowRISC contributors.
|
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Register Top module auto-generated by `reggen`
|
|
<%
|
|
from reggen import gen_rtl
|
|
from reggen.access import HwAccess, SwRdAccess, SwWrAccess
|
|
from reggen.lib import get_basename
|
|
from reggen.register import Register
|
|
from reggen.multi_register import MultiRegister
|
|
|
|
num_wins = len(rb.windows)
|
|
num_wins_width = ((num_wins+1).bit_length()) - 1
|
|
num_reg_dsp = 1 if rb.all_regs else 0
|
|
num_dsp = num_wins + num_reg_dsp
|
|
regs_flat = rb.flat_regs
|
|
max_regs_char = len("{}".format(len(regs_flat) - 1))
|
|
addr_width = rb.get_addr_width()
|
|
|
|
lblock = block.name.lower()
|
|
ublock = lblock.upper()
|
|
|
|
u_mod_base = mod_base.upper()
|
|
|
|
reg2hw_t = gen_rtl.get_iface_tx_type(block, if_name, False)
|
|
hw2reg_t = gen_rtl.get_iface_tx_type(block, if_name, True)
|
|
|
|
win_array_decl = f' [{num_wins}]' if num_wins > 1 else ''
|
|
|
|
# Calculate whether we're going to need an AW parameter. We use it if there
|
|
# are any registers (obviously). We also use it if there are any windows that
|
|
# don't start at zero and end at 1 << addr_width (see the "addr_checks"
|
|
# calculation below for where that comes from).
|
|
needs_aw = (bool(regs_flat) or
|
|
num_wins > 1 or
|
|
rb.windows and (
|
|
rb.windows[0].offset != 0 or
|
|
rb.windows[0].size_in_bytes != (1 << addr_width)))
|
|
|
|
|
|
common_data_intg_gen = 0 if rb.has_data_intg_passthru else 1
|
|
adapt_data_intg_gen = 1 if rb.has_data_intg_passthru else 0
|
|
assert common_data_intg_gen != adapt_data_intg_gen
|
|
%>
|
|
|
|
module ${mod_name} (
|
|
input logic clk_i,
|
|
input logic rst_ni,
|
|
|
|
// To HW
|
|
% if rb.get_n_bits(["q","qe","re"]):
|
|
output ${lblock}_reg_pkg::${reg2hw_t} reg2hw, // Write
|
|
% endif
|
|
% if rb.get_n_bits(["d","de"]):
|
|
input ${lblock}_reg_pkg::${hw2reg_t} hw2reg, // Read
|
|
% endif
|
|
|
|
input logic reg_we,
|
|
input logic reg_re,
|
|
input logic [31:0] reg_wdata,
|
|
input logic [ 3:0] reg_be,
|
|
input logic [31:0] reg_addr,
|
|
output logic [31:0] reg_rdata
|
|
);
|
|
|
|
import ${lblock}_reg_pkg::* ;
|
|
|
|
% if needs_aw:
|
|
localparam int AW = ${addr_width};
|
|
% endif
|
|
% if rb.all_regs:
|
|
localparam int DW = ${block.regwidth};
|
|
localparam int DBW = DW/8; // Byte Width
|
|
|
|
logic reg_error;
|
|
logic addrmiss, wr_err;
|
|
|
|
logic [DW-1:0] reg_rdata_next;
|
|
% endif
|
|
|
|
% if rb.all_regs:
|
|
assign reg_rdata = reg_rdata_next;
|
|
assign reg_error = wr_err;
|
|
|
|
// Define SW related signals
|
|
// Format: <reg>_<field>_{wd|we|qs}
|
|
// or <reg>_{wd|we|qs} if field == 1 or 0
|
|
% for r in regs_flat:
|
|
${reg_sig_decl(r)}\
|
|
% for f in r.fields:
|
|
<%
|
|
fld_suff = '_' + f.name.lower() if len(r.fields) > 1 else ''
|
|
sig_name = r.name.lower() + fld_suff
|
|
%>\
|
|
${field_sig_decl(f, sig_name, r.hwext, r.shadowed)}\
|
|
% endfor
|
|
% endfor
|
|
|
|
// Register instances
|
|
% for r in rb.all_regs:
|
|
######################## multiregister ###########################
|
|
% if isinstance(r, MultiRegister):
|
|
<%
|
|
k = 0
|
|
%>
|
|
% for sr in r.regs:
|
|
// Subregister ${k} of Multireg ${r.reg.name.lower()}
|
|
// R[${sr.name.lower()}]: V(${str(sr.hwext)})
|
|
% if len(sr.fields) == 1:
|
|
<%
|
|
f = sr.fields[0]
|
|
finst_name = sr.name.lower()
|
|
fsig_name = r.reg.name.lower() + "[%d]" % k
|
|
k = k + 1
|
|
%>
|
|
${finst_gen(sr, f, finst_name, fsig_name)}
|
|
% else:
|
|
% for f in sr.fields:
|
|
<%
|
|
finst_name = sr.name.lower() + "_" + f.name.lower()
|
|
if r.is_homogeneous():
|
|
fsig_name = r.reg.name.lower() + "[%d]" % k
|
|
k = k + 1
|
|
else:
|
|
fsig_name = r.reg.name.lower() + "[%d]" % k + "." + get_basename(f.name.lower())
|
|
%>
|
|
// F[${f.name.lower()}]: ${f.bits.msb}:${f.bits.lsb}
|
|
${finst_gen(sr, f, finst_name, fsig_name)}
|
|
% endfor
|
|
<%
|
|
if not r.is_homogeneous():
|
|
k += 1
|
|
%>
|
|
% endif
|
|
## for: mreg_flat
|
|
% endfor
|
|
######################## register with single field ###########################
|
|
% elif len(r.fields) == 1:
|
|
// R[${r.name.lower()}]: V(${str(r.hwext)})
|
|
<%
|
|
f = r.fields[0]
|
|
finst_name = r.name.lower()
|
|
fsig_name = r.name.lower()
|
|
%>
|
|
${finst_gen(r, f, finst_name, fsig_name)}
|
|
######################## register with multiple fields ###########################
|
|
% else:
|
|
// R[${r.name.lower()}]: V(${str(r.hwext)})
|
|
% for f in r.fields:
|
|
<%
|
|
finst_name = r.name.lower() + "_" + f.name.lower()
|
|
fsig_name = r.name.lower() + "." + f.name.lower()
|
|
%>
|
|
// F[${f.name.lower()}]: ${f.bits.msb}:${f.bits.lsb}
|
|
${finst_gen(r, f, finst_name, fsig_name)}
|
|
% endfor
|
|
% endif
|
|
|
|
## for: rb.all_regs
|
|
% endfor
|
|
logic [${len(regs_flat)-1}:0] addr_hit;
|
|
always_comb begin
|
|
addr_hit = '0;
|
|
% for i,r in enumerate(regs_flat):
|
|
addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${ublock}_${r.name.upper()}_OFFSET);
|
|
% endfor
|
|
end
|
|
|
|
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
|
|
|
|
% if regs_flat:
|
|
<%
|
|
# We want to signal wr_err if reg_be (the byte enable signal) is true for
|
|
# any bytes that aren't supported by a register. That's true if a
|
|
# addr_hit[i] and a bit is set in reg_be but not in *_PERMIT[i].
|
|
|
|
wr_err_terms = ['(addr_hit[{idx}] & (|({mod}_PERMIT[{idx}] & ~reg_be)))'
|
|
.format(idx=str(i).rjust(max_regs_char),
|
|
mod=u_mod_base)
|
|
for i in range(len(regs_flat))]
|
|
wr_err_expr = (' |\n' + (' ' * 15)).join(wr_err_terms)
|
|
%>\
|
|
// Check sub-word write is permitted
|
|
always_comb begin
|
|
wr_err = (reg_we &
|
|
(${wr_err_expr}));
|
|
end
|
|
% else:
|
|
assign wr_error = 1'b0;
|
|
% endif\
|
|
|
|
% for i, r in enumerate(regs_flat):
|
|
${reg_enable_gen(r, i)}\
|
|
% if len(r.fields) == 1:
|
|
${field_wd_gen(r.fields[0], r.name.lower(), r.hwext, r.shadowed, i)}\
|
|
% else:
|
|
% for f in r.fields:
|
|
${field_wd_gen(f, r.name.lower() + "_" + f.name.lower(), r.hwext, r.shadowed, i)}\
|
|
% endfor
|
|
% endif
|
|
% endfor
|
|
|
|
// Read data return
|
|
always_comb begin
|
|
reg_rdata_next = '0;
|
|
unique case (1'b1)
|
|
% for i, r in enumerate(regs_flat):
|
|
% if len(r.fields) == 1:
|
|
addr_hit[${i}]: begin
|
|
${rdata_gen(r.fields[0], r.name.lower())}\
|
|
end
|
|
|
|
% else:
|
|
addr_hit[${i}]: begin
|
|
% for f in r.fields:
|
|
${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\
|
|
% endfor
|
|
end
|
|
|
|
% endif
|
|
% endfor
|
|
default: begin
|
|
reg_rdata_next = '1;
|
|
end
|
|
endcase
|
|
end
|
|
% endif
|
|
|
|
// Unused signal tieoff
|
|
% if rb.all_regs:
|
|
|
|
// wdata / byte enable are not always fully used
|
|
// add a blanket unused statement to handle lint waivers
|
|
logic unused_wdata;
|
|
logic unused_be;
|
|
assign unused_wdata = ^reg_wdata;
|
|
assign unused_be = ^reg_be;
|
|
% else:
|
|
// devmode_i is not used if there are no registers
|
|
logic unused_devmode;
|
|
assign unused_devmode = ^devmode_i;
|
|
% endif
|
|
% if rb.all_regs:
|
|
|
|
% endif
|
|
endmodule
|
|
<%def name="str_bits_sv(bits)">\
|
|
% if bits.msb != bits.lsb:
|
|
${bits.msb}:${bits.lsb}\
|
|
% else:
|
|
${bits.msb}\
|
|
% endif
|
|
</%def>\
|
|
<%def name="str_arr_sv(bits)">\
|
|
% if bits.msb != bits.lsb:
|
|
[${bits.msb-bits.lsb}:0] \
|
|
% endif
|
|
</%def>\
|
|
<%def name="reg_sig_decl(reg)">\
|
|
% if reg.needs_re():
|
|
logic ${reg.name.lower()}_re;
|
|
% endif
|
|
% if reg.needs_we():
|
|
logic ${reg.name.lower()}_we;
|
|
% endif
|
|
</%def>\
|
|
<%def name="field_sig_decl(field, sig_name, hwext, shadowed)">\
|
|
% if field.swaccess.allows_read():
|
|
logic ${str_arr_sv(field.bits)}${sig_name}_qs;
|
|
% endif
|
|
% if field.swaccess.allows_write():
|
|
logic ${str_arr_sv(field.bits)}${sig_name}_wd;
|
|
% endif
|
|
</%def>\
|
|
<%def name="finst_gen(reg, field, finst_name, fsig_name)">\
|
|
<%
|
|
re_expr = f'{reg.name.lower()}_re' if field.swaccess.allows_read() else "1'b0"
|
|
|
|
if field.swaccess.allows_write():
|
|
# We usually use the REG_we signal, but use REG_re for RC fields
|
|
# (which get updated on a read, not a write)
|
|
we_suffix = 're' if field.swaccess.swrd() == SwRdAccess.RC else 'we'
|
|
we_signal = f'{reg.name.lower()}_{we_suffix}'
|
|
|
|
if reg.regwen:
|
|
we_expr = f'{we_signal} & {reg.regwen.lower()}_qs'
|
|
else:
|
|
we_expr = we_signal
|
|
wd_expr = f'{finst_name}_wd'
|
|
else:
|
|
we_expr = "1'b0"
|
|
wd_expr = "'0"
|
|
|
|
if field.hwaccess.allows_write():
|
|
de_expr = f'hw2reg.{fsig_name}.de'
|
|
d_expr = f'hw2reg.{fsig_name}.d'
|
|
else:
|
|
de_expr = "1'b0"
|
|
d_expr = "'0"
|
|
|
|
qre_expr = f'reg2hw.{fsig_name}.re' if reg.hwre or reg.shadowed else ""
|
|
|
|
if field.hwaccess.allows_read():
|
|
qe_expr = f'reg2hw.{fsig_name}.qe' if reg.hwqe else ''
|
|
q_expr = f'reg2hw.{fsig_name}.q'
|
|
else:
|
|
qe_expr = ''
|
|
q_expr = ''
|
|
|
|
qs_expr = f'{finst_name}_qs' if field.swaccess.allows_read() else ''
|
|
%>\
|
|
% if reg.hwext: ## if hwext, instantiate prim_subreg_ext
|
|
prim_subreg_ext #(
|
|
.DW (${field.bits.width()})
|
|
) u_${finst_name} (
|
|
.re (${re_expr}),
|
|
.we (${we_expr}),
|
|
.wd (${wd_expr}),
|
|
.d (${d_expr}),
|
|
.qre (${qre_expr}),
|
|
.qe (${qe_expr}),
|
|
.q (${q_expr}),
|
|
.qs (${qs_expr})
|
|
);
|
|
% else:
|
|
<%
|
|
# This isn't a field in a hwext register. Instantiate prim_subreg,
|
|
# prim_subreg_shadow or constant assign.
|
|
|
|
resval_expr = f"{field.bits.width()}'h{field.resval or 0:x}"
|
|
is_const_reg = not (field.hwaccess.allows_read() or
|
|
field.hwaccess.allows_write() or
|
|
field.swaccess.allows_write() or
|
|
field.swaccess.swrd() != SwRdAccess.RD)
|
|
|
|
subreg_block = 'prim_subreg' + ('_shadowed' if reg.shadowed else '')
|
|
%>\
|
|
% if is_const_reg:
|
|
// constant-only read
|
|
assign ${finst_name}_qs = ${resval_expr};
|
|
% else:
|
|
${subreg_block} #(
|
|
.DW (${field.bits.width()}),
|
|
.SWACCESS("${field.swaccess.value[1].name.upper()}"),
|
|
.RESVAL (${resval_expr})
|
|
) u_${finst_name} (
|
|
.clk_i (clk_i),
|
|
.rst_ni (rst_ni),
|
|
|
|
// from register interface
|
|
% if reg.shadowed:
|
|
.re (${re_expr}),
|
|
% endif
|
|
.we (${we_expr}),
|
|
.wd (${wd_expr}),
|
|
|
|
// from internal hardware
|
|
.de (${de_expr}),
|
|
.d (${d_expr}),
|
|
|
|
// to internal hardware
|
|
.qe (${qe_expr}),
|
|
.q (${q_expr}),
|
|
|
|
// to register interface (read)
|
|
% if not reg.shadowed:
|
|
.qs (${qs_expr})
|
|
% else:
|
|
.qs (${qs_expr}),
|
|
|
|
// Shadow register error conditions
|
|
.err_update (reg2hw.${fsig_name}.err_update),
|
|
.err_storage (reg2hw.${fsig_name}.err_storage)
|
|
% endif
|
|
);
|
|
% endif ## end non-constant prim_subreg
|
|
% endif
|
|
</%def>\
|
|
<%def name="reg_enable_gen(reg, idx)">\
|
|
% if reg.needs_re():
|
|
assign ${reg.name.lower()}_re = addr_hit[${idx}] & reg_re & !reg_error;
|
|
% endif
|
|
% if reg.needs_we():
|
|
assign ${reg.name.lower()}_we = addr_hit[${idx}] & reg_we & !reg_error;
|
|
% endif
|
|
</%def>\
|
|
<%def name="field_wd_gen(field, sig_name, hwext, shadowed, idx)">\
|
|
<%
|
|
needs_wd = field.swaccess.allows_write()
|
|
space = '\n' if needs_wd or needs_re else ''
|
|
%>\
|
|
${space}\
|
|
% if needs_wd:
|
|
% if field.swaccess.swrd() == SwRdAccess.RC:
|
|
assign ${sig_name}_wd = '1;
|
|
% else:
|
|
assign ${sig_name}_wd = reg_wdata[${str_bits_sv(field.bits)}];
|
|
% endif
|
|
% endif
|
|
</%def>\
|
|
<%def name="rdata_gen(field, sig_name)">\
|
|
% if field.swaccess.allows_read():
|
|
reg_rdata_next[${str_bits_sv(field.bits)}] = ${sig_name}_qs;
|
|
% else:
|
|
reg_rdata_next[${str_bits_sv(field.bits)}] = '0;
|
|
% endif
|
|
</%def>\
|