// 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: __{wd|we|qs} // or _{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 name="str_arr_sv(bits)">\ % if bits.msb != bits.lsb: [${bits.msb-bits.lsb}:0] \ % endif \ <%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 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 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 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 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 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 \