From d0b70aef0a481ce35f290a8960e73156d33caeec Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 13 Jun 2024 23:07:01 +0200 Subject: [PATCH] [topgen] Support for parametrized inter-module signals Signed-off-by: Robert Schilling --- hw/top_darjeeling/templates/toplevel.sv.tpl | 64 ++++++++++++++-- .../data/autogen/top_earlgrey.gen.hjson | 75 +++++++++++++++++++ hw/top_earlgrey/rtl/autogen/top_earlgrey.sv | 3 + hw/top_earlgrey/templates/toplevel.sv.tpl | 65 ++++++++++++++-- util/reggen/gen_cfg_md.py | 6 +- util/reggen/inter_signal.py | 30 ++++++-- util/reggen/ip_block.py | 1 + util/reggen/params.py | 22 +++--- util/topgen.py | 4 +- util/topgen/intermodule.py | 52 ++++++++----- util/topgen/merge.py | 8 ++ 11 files changed, 278 insertions(+), 52 deletions(-) diff --git a/hw/top_darjeeling/templates/toplevel.sv.tpl b/hw/top_darjeeling/templates/toplevel.sv.tpl index 0b0962f2882ac..b1eacaba9cee8 100644 --- a/hw/top_darjeeling/templates/toplevel.sv.tpl +++ b/hw/top_darjeeling/templates/toplevel.sv.tpl @@ -5,6 +5,7 @@ ${gencmd} <% import re import topgen.lib as lib +from reggen.params import Parameter from topgen.clocks import Clocks from topgen.resets import Resets @@ -22,7 +23,13 @@ num_dio_total = top['pinmux']['io_counts']['dedicated']['inouts'] + \ top['pinmux']['io_counts']['dedicated']['inputs'] + \ top['pinmux']['io_counts']['dedicated']['outputs'] -num_im = sum([x["width"] if "width" in x else 1 for x in top["inter_signal"]["external"]]) +num_im = 0 +for x in top["inter_signal"]["external"]: + width = 1 + if "width" in x: + width = (x["width"].default + if isinstance(x["width"], Parameter) else x["width"]) + num_im += width max_sigwidth = max([x["width"] if "width" in x else 1 for x in top["pinmux"]["ios"]]) max_sigwidth = len("{}".format(max_sigwidth)) @@ -54,7 +61,7 @@ module top_${top["name"]} #( <% continue %> % endif // parameters for ${m['name']} - % for p_exp in [p for p in m["param_list"] if p.get("expose") == "true" ]: + % for p_exp in [p for p in m["param_list"] if p.get("local") == "false" and p.get("expose") == "true" ]: <% p_type = p_exp.get('type') p_type_word = p_type + ' ' if p_type else '' @@ -69,7 +76,7 @@ module top_${top["name"]} #( params_follow = not loop.last or loop.parent.index < last_modidx_with_params comma_char = ',' if params_follow else '' %>\ - % if 12 + len(p_lhs) + 3 + len(p_rhs) + 1 < 100: + % if 12 + len(p_lhs) + 3 + len(str(p_rhs)) + 1 < 100: parameter ${p_lhs} = ${p_rhs}${comma_char} % else: parameter ${p_lhs} = @@ -101,7 +108,11 @@ module top_${top["name"]} #( // Inter-module Signal External type % for sig in top["inter_signal"]["external"]: + % if isinstance(sig["width"], Parameter): + ${lib.get_direction(sig)} ${lib.im_defname(sig)} [${sig["width"].name_top}-1:0] ${sig["signame"]}, + % else: ${lib.get_direction(sig)} ${lib.im_defname(sig)} ${lib.bitarray(sig["width"],1)} ${sig["signame"]}, + % endif % endfor % endif @@ -127,6 +138,34 @@ module top_${top["name"]} #( // Compile-time random constants import top_${top["name"]}_rnd_cnst_pkg::*; + // Local Parameters +% for m in top["module"]: + % if not lib.is_inst(m): +<% continue %> + % endif +<% + localparams = [p for p in m["param_list"] if p.get("local") == "true" and p.get("expose") == "true"] + if not len(localparams): + continue +%>\ + // local parameters for ${m['name']} + % for p_exp in localparams: +<% + p_type = p_exp.get('type') + p_type_word = p_type + ' ' if p_type else '' + + p_lhs = f'{p_type_word}{p_exp["name_top"]}' + p_rhs = p_exp['default'] +%>\ + % if 13 + len(p_lhs) + 3 + len(str(p_rhs)) + 1 < 100: + localparam ${p_lhs} = ${p_rhs}; + % else: + localparam ${p_lhs} = + ${p_rhs}; + % endif + % endfor +% endfor + // Signals logic [${num_mio_inputs - 1}:0] mio_p2d; logic [${num_mio_outputs - 1}:0] mio_d2p; @@ -193,7 +232,11 @@ module top_${top["name"]} #( // define inter-module signals % endif % for sig in top["inter_signal"]["definitions"]: + % if isinstance(sig["width"], Parameter): + ${lib.im_defname(sig)} [${sig["width"].name_top}-1:0] ${sig["signame"]}; + % else: ${lib.im_defname(sig)} ${lib.bitarray(sig["width"],1)} ${sig["signame"]}; + % endif % endfor ## Mixed connection to port @@ -219,19 +262,28 @@ module top_${top["name"]} #( ## Partial inter-module definition tie-off // define partial inter-module tie-off % for sig in unused_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): ${lib.im_defname(sig)} unused_${sig["signame"]}${idx}; % endfor % endfor // assign partial inter-module tie-off % for sig in unused_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): assign unused_${sig["signame"]}${idx} = ${sig["signame"]}[${idx}]; % endfor % endfor % for sig in undriven_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): assign ${sig["signame"]}[${idx}] = ${sig["default"]}; % endfor % endfor diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index 9465df162333e..c9a2f3033b2b4 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -882,6 +882,7 @@ desc: Instantiates 2-flop synchronizers on all GPIO inputs if set to 1. type: bit default: "1" + local: "false" expose: "true" name_top: GpioGpioAsyncOn } @@ -890,6 +891,7 @@ desc: Enable HW straps sampling logic for GPIO inputs at initial cold boot type: bit default: "0" + local: "false" expose: "true" name_top: GpioGpioAsHwStrapsEn } @@ -977,6 +979,7 @@ desc: Sram Entries. Word size is 32bit width. type: spi_device_pkg::sram_type_e default: spi_device_pkg::DefaultSramType + local: "false" expose: "true" name_top: SpiDeviceSramType } @@ -1091,6 +1094,7 @@ ''' type: int default: "0" + local: "false" expose: "true" name_top: I2c0InputDelayCycles } @@ -1183,6 +1187,7 @@ ''' type: int default: "0" + local: "false" expose: "true" name_top: I2c1InputDelayCycles } @@ -1275,6 +1280,7 @@ ''' type: int default: "0" + local: "false" expose: "true" name_top: I2c2InputDelayCycles } @@ -1483,6 +1489,7 @@ desc: VMEM file to initialize the OTP macro. type: "" default: '''""''' + local: "false" expose: "true" name_top: OtpCtrlMemInitFile } @@ -1930,6 +1937,7 @@ ''' type: bit default: top_pkg::SecVolatileRawUnlockEn + local: "false" expose: "true" name_top: SecLcCtrlVolatileRawUnlockEn } @@ -1938,6 +1946,7 @@ desc: When 1, a TLUL-based DMI interface is used. When 0, a JTAG TAP is used. type: bit default: "0" + local: "false" expose: "true" name_top: LcCtrlUseDmiInterface } @@ -2006,6 +2015,7 @@ desc: Chip generation number. type: logic [15:0] default: 16'h 4001 + local: "false" expose: "true" name_top: LcCtrlSiliconCreatorId } @@ -2014,6 +2024,7 @@ desc: Chip revision number. type: logic [15:0] default: 16'h 0002 + local: "false" expose: "true" name_top: LcCtrlProductId } @@ -2022,6 +2033,7 @@ desc: Chip revision number. type: logic [7:0] default: 8'h 01 + local: "false" expose: "true" name_top: LcCtrlRevisionId } @@ -2030,6 +2042,7 @@ desc: JTAG ID code. type: logic [31:0] default: jtag_id_pkg::LC_CTRL_JTAG_IDCODE + local: "false" expose: "true" name_top: LcCtrlIdcodeValue } @@ -2821,6 +2834,7 @@ desc: Stub out the core of entropy_src logic type: bit default: "0" + local: "false" expose: "true" name_top: UsbdevStub } @@ -2829,6 +2843,7 @@ desc: Maximum number of microseconds for the differential receiver to become operational type: int default: "100" + local: "false" expose: "true" name_top: UsbdevRcvrWakeTimeUs } @@ -3484,6 +3499,7 @@ ''' type: bit default: 1'b1 + local: "false" expose: "true" name_top: SecRstmgrAonCheck } @@ -3492,6 +3508,7 @@ desc: The maximum synchronization delay for parent / child reset checks. type: int default: "2" + local: "false" expose: "true" name_top: SecRstmgrAonMaxSyncDelay } @@ -4240,6 +4257,7 @@ ''' type: bit default: top_pkg::SecVolatileRawUnlockEn + local: "false" expose: "true" name_top: SecPinmuxAonVolatileRawUnlockEn } @@ -4248,6 +4266,7 @@ desc: Target specific pinmux configuration. type: pinmux_pkg::target_cfg_t default: pinmux_pkg::DefaultTargetCfg + local: "false" expose: "true" name_top: PinmuxAonTargetCfg } @@ -5119,6 +5138,7 @@ desc: Support execution from SRAM type: bit default: "0" + local: "false" expose: "true" name_top: SramCtrlRetAonInstrExec } @@ -5127,6 +5147,7 @@ desc: Number of PRINCE half rounds for the SRAM scrambling feature type: int default: "3" + local: "false" expose: "true" name_top: SramCtrlRetAonNumPrinceRoundsHalf } @@ -5350,6 +5371,7 @@ desc: Compile-time option to enable flash scrambling type: bit default: "1" + local: "false" expose: "true" name_top: SecFlashCtrlScrambleEn } @@ -5358,6 +5380,7 @@ desc: Depth of program fifo type: int default: "4" + local: "false" expose: "true" name_top: FlashCtrlProgFifoDepth } @@ -5366,6 +5389,7 @@ desc: Depth of read fifo type: int default: "16" + local: "false" expose: "true" name_top: FlashCtrlRdFifoDepth } @@ -5730,6 +5754,7 @@ desc: RISC-V debug module JTAG ID code. type: logic [31:0] default: jtag_id_pkg::RV_DM_JTAG_IDCODE + local: "false" expose: "true" name_top: RvDmIdcodeValue } @@ -5738,6 +5763,7 @@ desc: When 1, a TLUL-based DMI interface is used. When 0, a JTAG TAP is used. type: bit default: "0" + local: "false" expose: "true" name_top: RvDmUseDmiInterface } @@ -5753,6 +5779,7 @@ ''' type: bit default: 1'b0 + local: "false" expose: "true" name_top: SecRvDmVolatileRawUnlockEn } @@ -6158,6 +6185,7 @@ desc: Disable (0) or enable (1) support for 192-bit key lengths (AES-192). type: bit default: 1'b1 + local: "false" expose: "false" name_top: AesAES192Enable } @@ -6170,6 +6198,7 @@ ''' type: bit default: "1" + local: "false" expose: "true" name_top: SecAesMasking } @@ -6178,6 +6207,7 @@ desc: Selection of the S-Box implementation. See aes_pkg.sv. type: aes_pkg::sbox_impl_e default: aes_pkg::SBoxImplDom + local: "false" expose: "true" name_top: SecAesSBoxImpl } @@ -6190,6 +6220,7 @@ ''' type: int unsigned default: "0" + local: "false" expose: "true" name_top: SecAesStartTriggerDelay } @@ -6203,6 +6234,7 @@ ''' type: bit default: 1'b0 + local: "false" expose: "true" name_top: SecAesAllowForcingMasks } @@ -6216,6 +6248,7 @@ ''' type: bit default: 1'b0 + local: "false" expose: "true" name_top: SecAesSkipPRNGReseeding } @@ -6449,6 +6482,7 @@ desc: Disable(0) or enable(1) first-order masking of Keccak round. type: bit default: "1" + local: "false" expose: "true" name_top: KmacEnMasking } @@ -6463,6 +6497,7 @@ ''' type: bit default: "0" + local: "false" expose: "true" name_top: KmacSwKeyMasked } @@ -6477,6 +6512,7 @@ ''' type: int default: "0" + local: "false" expose: "true" name_top: SecKmacCmdDelay } @@ -6490,6 +6526,7 @@ ''' type: bit default: "0" + local: "false" expose: "true" name_top: SecKmacIdleAcceptSwMsg } @@ -6694,6 +6731,7 @@ desc: Stub out the core of Otbn logic type: bit default: "0" + local: "false" expose: "true" name_top: OtbnStub } @@ -6702,6 +6740,7 @@ desc: Selection of the register file implementation. See otbn_pkg.sv. type: otbn_pkg::regfile_e default: otbn_pkg::RegFileFF + local: "false" expose: "true" name_top: OtbnRegFile } @@ -6725,6 +6764,7 @@ ''' type: bit default: "0" + local: "false" expose: "true" name_top: SecOtbnMuteUrnd } @@ -6738,6 +6778,7 @@ ''' type: bit default: "0" + local: "false" expose: "true" name_top: SecOtbnSkipUrndReseedAtStart } @@ -6941,6 +6982,7 @@ ''' type: bit default: "0" + local: "false" expose: "true" name_top: KeymgrUseOtpSeedsInsteadOfFlash } @@ -6949,6 +6991,7 @@ desc: Flag indicating with kmac masking is enabled type: bit default: "1" + local: "false" expose: "true" name_top: KeymgrKmacEnMasking } @@ -7321,6 +7364,7 @@ desc: Selection of the S-Box implementation. See aes_pkg.sv. type: aes_pkg::sbox_impl_e default: aes_pkg::SBoxImplCanright + local: "false" expose: "true" name_top: CsrngSBoxImpl } @@ -7452,6 +7496,7 @@ desc: Number of 384-bit entries in the esfinal FIFO type: int default: "3" + local: "false" expose: "true" name_top: EntropySrcEsFifoDepth } @@ -7460,6 +7505,7 @@ desc: Number of 32-bit entries in the distr FIFO type: int unsigned default: "2" + local: "false" expose: "true" name_top: EntropySrcDistrFifoDepth } @@ -7468,6 +7514,7 @@ desc: Stub out the core of entropy_src logic type: bit default: "0" + local: "false" expose: "true" name_top: EntropySrcStub } @@ -7873,6 +7920,7 @@ desc: Support execution from SRAM type: bit default: "1" + local: "false" expose: "true" name_top: SramCtrlMainInstrExec } @@ -7881,6 +7929,7 @@ desc: Number of PRINCE half rounds for the SRAM scrambling feature type: int default: "2" + local: "false" expose: "true" name_top: SramCtrlMainNumPrinceRoundsHalf } @@ -8034,6 +8083,7 @@ desc: Contents of ROM type: "" default: '''""''' + local: "false" expose: "true" name_top: RomCtrlBootRomInitFile } @@ -8068,6 +8118,7 @@ ''' type: bit default: 1'b0 + local: "false" expose: "true" name_top: SecRomCtrlDisableScrambling } @@ -8290,6 +8341,7 @@ desc: Enable PMP type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexPMPEnable } @@ -8298,6 +8350,7 @@ desc: PMP Granularity type: int unsigned default: "0" + local: "false" expose: "true" name_top: RvCoreIbexPMPGranularity } @@ -8306,6 +8359,7 @@ desc: PMP number of regions type: int unsigned default: "16" + local: "false" expose: "true" name_top: RvCoreIbexPMPNumRegions } @@ -8314,6 +8368,7 @@ desc: "Number of the MHPM counter " type: int unsigned default: "2" + local: "false" expose: "true" name_top: RvCoreIbexMHPMCounterNum } @@ -8322,6 +8377,7 @@ desc: "Width of the MHPM Counter " type: int unsigned default: "32" + local: "false" expose: "true" name_top: RvCoreIbexMHPMCounterWidth } @@ -8331,6 +8387,7 @@ type: ibex_pkg::pmp_cfg_t unpacked_dimensions: "[16]" default: ibex_pmp_reset_pkg::PmpCfgRst + local: "false" expose: "true" name_top: RvCoreIbexPMPRstCfg } @@ -8340,6 +8397,7 @@ type: logic [33:0] unpacked_dimensions: "[16]" default: ibex_pmp_reset_pkg::PmpAddrRst + local: "false" expose: "true" name_top: RvCoreIbexPMPRstAddr } @@ -8348,6 +8406,7 @@ desc: Reset value of MSECCFG CSR type: ibex_pkg::pmp_mseccfg_t default: ibex_pmp_reset_pkg::PmpMseccfgRst + local: "false" expose: "true" name_top: RvCoreIbexPMPRstMsecCfg } @@ -8356,6 +8415,7 @@ desc: RV32E type: bit default: "0" + local: "false" expose: "true" name_top: RvCoreIbexRV32E } @@ -8364,6 +8424,7 @@ desc: RV32M type: ibex_pkg::rv32m_e default: ibex_pkg::RV32MSingleCycle + local: "false" expose: "true" name_top: RvCoreIbexRV32M } @@ -8372,6 +8433,7 @@ desc: RV32B type: ibex_pkg::rv32b_e default: ibex_pkg::RV32BOTEarlGrey + local: "false" expose: "true" name_top: RvCoreIbexRV32B } @@ -8380,6 +8442,7 @@ desc: Reg file type: ibex_pkg::regfile_e default: ibex_pkg::RegFileFF + local: "false" expose: "true" name_top: RvCoreIbexRegFile } @@ -8388,6 +8451,7 @@ desc: Branch target ALU type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexBranchTargetALU } @@ -8396,6 +8460,7 @@ desc: Write back stage type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexWritebackStage } @@ -8404,6 +8469,7 @@ desc: Instruction cache type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexICache } @@ -8412,6 +8478,7 @@ desc: Instruction cache ECC type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexICacheECC } @@ -8420,6 +8487,7 @@ desc: Scramble instruction cach type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexICacheScramble } @@ -8428,6 +8496,7 @@ desc: Branch predictor type: bit default: "0" + local: "false" expose: "true" name_top: RvCoreIbexBranchPredictor } @@ -8436,6 +8505,7 @@ desc: Enable degug trigger type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexDbgTriggerEn } @@ -8444,6 +8514,7 @@ desc: Number of debug hardware break type: int default: "4" + local: "false" expose: "true" name_top: RvCoreIbexDbgHwBreakNum } @@ -8452,6 +8523,7 @@ desc: "Width of the MHPM Counter " type: bit default: "1" + local: "false" expose: "true" name_top: RvCoreIbexSecureIbex } @@ -8460,6 +8532,7 @@ desc: Halt address type: int unsigned default: tl_main_pkg::ADDR_SPACE_RV_DM__MEM + dm::HaltAddress[31:0] + local: "false" expose: "true" name_top: RvCoreIbexDmHaltAddr } @@ -8468,6 +8541,7 @@ desc: Exception address type: int unsigned default: tl_main_pkg::ADDR_SPACE_RV_DM__MEM + dm::ExceptionAddress[31:0] + local: "false" expose: "true" name_top: RvCoreIbexDmExceptionAddr } @@ -8476,6 +8550,7 @@ desc: Pipe line type: bit default: "0" + local: "false" expose: "true" name_top: RvCoreIbexPipeLine } diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 32ba8fbfb475b..a4fa3db4c2a10 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv @@ -227,6 +227,8 @@ module top_earlgrey #( // Compile-time random constants import top_earlgrey_rnd_cnst_pkg::*; + // Local Parameters + // Signals logic [56:0] mio_p2d; logic [74:0] mio_d2p; @@ -784,6 +786,7 @@ module top_earlgrey #( edn_pkg::edn_rsp_t unused_edn1_edn_rsp7; // assign partial inter-module tie-off + assign unused_otp_ctrl_sram_otp_key_rsp3 = otp_ctrl_sram_otp_key_rsp[3]; assign unused_edn1_edn_rsp1 = edn1_edn_rsp[1]; assign unused_edn1_edn_rsp2 = edn1_edn_rsp[2]; diff --git a/hw/top_earlgrey/templates/toplevel.sv.tpl b/hw/top_earlgrey/templates/toplevel.sv.tpl index 87e9debf272e8..32e5c90458ec7 100644 --- a/hw/top_earlgrey/templates/toplevel.sv.tpl +++ b/hw/top_earlgrey/templates/toplevel.sv.tpl @@ -5,6 +5,7 @@ ${gencmd} <% import re import topgen.lib as lib +from reggen.params import Parameter from topgen.clocks import Clocks from topgen.resets import Resets @@ -22,7 +23,13 @@ num_dio_total = top['pinmux']['io_counts']['dedicated']['inouts'] + \ top['pinmux']['io_counts']['dedicated']['inputs'] + \ top['pinmux']['io_counts']['dedicated']['outputs'] -num_im = sum([x["width"] if "width" in x else 1 for x in top["inter_signal"]["external"]]) +num_im = 0 +for x in top["inter_signal"]["external"]: + width = 1 + if "width" in x: + width = (x["width"].default + if isinstance(x["width"], Parameter) else x["width"]) + num_im += width max_sigwidth = max([x["width"] if "width" in x else 1 for x in top["pinmux"]["ios"]]) max_sigwidth = len("{}".format(max_sigwidth)) @@ -52,7 +59,7 @@ module top_${top["name"]} #( <% continue %> % endif // parameters for ${m['name']} - % for p_exp in [p for p in m["param_list"] if p.get("expose") == "true" ]: + % for p_exp in [p for p in m["param_list"] if p.get("local") == "false" and p.get("expose") == "true" ]: <% p_type = p_exp.get('type') p_type_word = p_type + ' ' if p_type else '' @@ -67,7 +74,7 @@ module top_${top["name"]} #( params_follow = not loop.last or loop.parent.index < last_modidx_with_params comma_char = ',' if params_follow else '' %>\ - % if 12 + len(p_lhs) + 3 + len(p_rhs) + 1 < 100: + % if 12 + len(p_lhs) + 3 + len(str(p_rhs)) + 1 < 100: parameter ${p_lhs} = ${p_rhs}${comma_char} % else: parameter ${p_lhs} = @@ -99,7 +106,11 @@ module top_${top["name"]} #( // Inter-module Signal External type % for sig in top["inter_signal"]["external"]: + % if isinstance(sig["width"], Parameter): + ${lib.get_direction(sig)} ${lib.im_defname(sig)} [${sig["width"].name_top}-1:0] ${sig["signame"]}, + % else: ${lib.get_direction(sig)} ${lib.im_defname(sig)} ${lib.bitarray(sig["width"],1)} ${sig["signame"]}, + % endif % endfor % endif @@ -125,6 +136,34 @@ module top_${top["name"]} #( // Compile-time random constants import top_${top["name"]}_rnd_cnst_pkg::*; + // Local Parameters +% for m in top["module"]: + % if not lib.is_inst(m): +<% continue %> + % endif +<% + localparams = [p for p in m["param_list"] if p.get("local") == "true" and p.get("expose") == "true"] + if not len(localparams): + continue +%>\ + // local parameters for ${m['name']} + % for p_exp in localparams: +<% + p_type = p_exp.get('type') + p_type_word = p_type + ' ' if p_type else '' + + p_lhs = f'{p_type_word}{p_exp["name_top"]}' + p_rhs = p_exp['default'] +%>\ + % if 13 + len(p_lhs) + 3 + len(str(p_rhs)) + 1 < 100: + localparam ${p_lhs} = ${p_rhs}; + % else: + localparam ${p_lhs} = + ${p_rhs}; + % endif + % endfor +% endfor + // Signals logic [${num_mio_inputs - 1}:0] mio_p2d; logic [${num_mio_outputs - 1}:0] mio_d2p; @@ -191,7 +230,11 @@ module top_${top["name"]} #( // define inter-module signals % endif % for sig in top["inter_signal"]["definitions"]: + % if isinstance(sig["width"], Parameter): + ${lib.im_defname(sig)} [${sig["width"].name_top}-1:0] ${sig["signame"]}; + % else: ${lib.im_defname(sig)} ${lib.bitarray(sig["width"],1)} ${sig["signame"]}; + % endif % endfor ## Mixed connection to port @@ -217,19 +260,29 @@ module top_${top["name"]} #( ## Partial inter-module definition tie-off // define partial inter-module tie-off % for sig in unused_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): ${lib.im_defname(sig)} unused_${sig["signame"]}${idx}; % endfor % endfor // assign partial inter-module tie-off + % for sig in unused_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): assign unused_${sig["signame"]}${idx} = ${sig["signame"]}[${idx}]; % endfor % endfor % for sig in undriven_im_defs: - % for idx in range(sig['end_idx'], sig['width']): +<% + width = sig['width'].default if isinstance(sig['width'], Parameter) else sig['width'] +%>\ + % for idx in range(sig['end_idx'], width): assign ${sig["signame"]}[${idx}] = ${sig["default"]}; % endfor % endfor diff --git a/util/reggen/gen_cfg_md.py b/util/reggen/gen_cfg_md.py index 0ece58e3a5fcb..ca3629d41bff4 100644 --- a/util/reggen/gen_cfg_md.py +++ b/util/reggen/gen_cfg_md.py @@ -9,6 +9,7 @@ from reggen.md_helpers import ( title, url, italic, coderef, regref_to_link, name_width, table, list_item, ) +from reggen.params import Parameter def gen_cfg_md(cfgs: IpBlock, output: TextIO, register_file: Optional[str] = None) -> None: @@ -78,7 +79,10 @@ def gen_cfg_md(cfgs: IpBlock, output: TextIO, register_file: Optional[str] = Non pkg_struct = ims.package + "::" + ims.struct if ims.package is not None else ims.struct sig_type = ims.signal_type act = ims.act - width = str(ims.width) if ims.width is not None else "1" + if isinstance(ims.width, Parameter): + width = ims.width.name + else: + width = str(ims.width) if ims.width is not None else "1" desc = ims.desc if ims.desc is not None else "" rows.append([name, pkg_struct, sig_type, act, width, desc]) diff --git a/util/reggen/inter_signal.py b/util/reggen/inter_signal.py index e1cbd19339827..d9d98a12a4890 100644 --- a/util/reggen/inter_signal.py +++ b/util/reggen/inter_signal.py @@ -2,10 +2,11 @@ # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -from typing import Dict, Optional +from typing import Dict, Optional, Union from reggen.lib import (check_keys, check_name, check_str, check_optional_str, check_int) +from reggen.params import ReggenParams, Parameter class InterSignal: @@ -16,9 +17,13 @@ def __init__(self, package: Optional[str], signal_type: str, act: str, - width: int, + width: Union[int, Parameter], default: Optional[str]): - assert 0 < width + if isinstance(width, Parameter): + if isinstance(width.default, int): + assert 0 < width.default + else: + assert 0 < width self.name = name self.desc = desc self.struct = struct @@ -29,7 +34,7 @@ def __init__(self, self.default = default @staticmethod - def from_raw(what: str, raw: object) -> 'InterSignal': + def from_raw(params: ReggenParams, what: str, raw: object) -> 'InterSignal': rd = check_keys(raw, what, ['name', 'struct', 'type', 'act'], ['desc', 'package', 'width', 'default']) @@ -52,12 +57,23 @@ def from_raw(what: str, raw: object) -> 'InterSignal': signal_type = check_name(rd['type'], 'type field of ' + what) act = check_name(rd['act'], 'act field of ' + what) - width = check_int(rd.get('width', 1), 'width field of ' + what) - if width <= 0: - raise ValueError('width field of {} is not positive.'.format(what)) default = check_optional_str(rd.get('default'), 'default field of ' + what) + width: Union[int, Parameter] = 1 + width_p = params.get(rd.get('width'), 1) + if isinstance(width_p, Parameter): + width_p.default = check_int(width_p.default, 'width field of ' + what) + if width_p.default <= 0: + raise ValueError('width field of {} is not positive.'.format(what)) + # Parameter must be exposed to create a top-level (local) param + if not width_p.expose: + raise ValueError('width field of {} is not exposed.'.format(what)) + width = width_p + else: + width = check_int(rd.get('width', 1), 'width field of ' + what) + if width <= 0: + raise ValueError('width field of {} is not positive.'.format(what)) return InterSignal(name, desc, struct, package, signal_type, act, width, default) diff --git a/util/reggen/ip_block.py b/util/reggen/ip_block.py index 93cb3718faef3..4b55035bc0a03 100644 --- a/util/reggen/ip_block.py +++ b/util/reggen/ip_block.py @@ -340,6 +340,7 @@ def from_raw(param_defaults: List[Tuple[str, str]], 'inter_signal_list field') inter_signals = [ InterSignal.from_raw( + params, 'entry {} of the inter_signal_list field'.format(idx + 1), entry) for idx, entry in enumerate(r_inter_signals) ] diff --git a/util/reggen/params.py b/util/reggen/params.py index dcfe68fb82999..a790370fc0566 100644 --- a/util/reggen/params.py +++ b/util/reggen/params.py @@ -4,7 +4,7 @@ import re from collections.abc import MutableMapping -from typing import Dict, Iterator, List, Optional, Tuple +from typing import Dict, Iterator, List, Optional, Tuple, Union from reggen.lib import check_keys, check_str, check_int, check_bool, check_list @@ -45,7 +45,7 @@ def apply_default(self, value: str) -> None: 'default value for parameter {} ' '(which has type {})' .format(self.name, self.param_type)) - self.default = value + self.default: Union[str, int] = value def as_dict(self) -> Dict[str, object]: rd = {} # type: Dict[str, object] @@ -90,14 +90,17 @@ def __init__(self, param_type: str, unpacked_dimensions: Optional[str], default: str, + local: bool, expose: bool): super().__init__(name, desc, param_type, unpacked_dimensions) self.default = default + self.local = local self.expose = expose def as_dict(self) -> Dict[str, object]: rd = super().as_dict() rd['default'] = self.default + rd['local'] = 'true' if self.local else 'false' rd['expose'] = 'true' if self.expose else 'false' return rd @@ -276,16 +279,12 @@ def _parse_parameter(where: str, raw: object) -> BaseParam: 'default field of {}, (an integer parameter)' .format(name)) - if local: - if expose: - raise ValueError('At {}, the localparam {} cannot be exposed to ' - 'the top-level.' - .format(where, name)) - return LocalParam(name, desc, param_type, unpacked_dimensions, - value=default) + if local and expose: + return Parameter(name, desc, param_type, unpacked_dimensions, default, local, expose) + elif local: + return LocalParam(name, desc, param_type, unpacked_dimensions, default) else: - return Parameter(name, desc, param_type, unpacked_dimensions, - default, expose) + return Parameter(name, desc, param_type, unpacked_dimensions, default, local, expose) # Note: With a modern enough Python, we'd like this to derive from @@ -402,4 +401,5 @@ def get_localparams(self) -> List[LocalParam]: for param in self.by_name.values(): if isinstance(param, LocalParam): ret.append(param) + return ret diff --git a/util/topgen.py b/util/topgen.py index b1688d160d0c8..b68a6607b8009 100755 --- a/util/topgen.py +++ b/util/topgen.py @@ -26,6 +26,7 @@ from reggen.countermeasure import CounterMeasure from reggen.inter_signal import InterSignal from reggen.ip_block import IpBlock +from reggen.params import ReggenParams from reggen.lib import check_list from topgen import get_hjsonobj_xbars from topgen import intermodule as im @@ -151,6 +152,7 @@ def generate_xbars(top: Dict[str, object], out_path: Path) -> None: "inter_signal_list field") obj["inter_signal_list"] = [ InterSignal.from_raw( + ReggenParams(), "entry {} of the inter_signal_list field".format(idx + 1), entry) for idx, entry in enumerate(r_inter_signal_list) ] @@ -1134,7 +1136,7 @@ def main(): """.format(top_name=top_name, seed=completecfg["rnd_cnst_seed"]) genhjson_path.write_text(genhdr + gencmd + - hjson.dumps(completecfg, for_json=True) + '\n') + hjson.dumps(completecfg, for_json=True, default=vars) + '\n') # Generate Rust toplevel definitions if not args.no_rust: diff --git a/util/topgen/intermodule.py b/util/topgen/intermodule.py index 7b2938d751f10..194dd70f73dfe 100644 --- a/util/topgen/intermodule.py +++ b/util/topgen/intermodule.py @@ -10,6 +10,7 @@ from reggen.ip_block import IpBlock from reggen.inter_signal import InterSignal +from reggen.params import Parameter from reggen.validate import check_int from topgen import lib @@ -641,20 +642,20 @@ def check_intermodule_field(sig: OrderedDict, name=sig['name'])) error += 1 # Check 'width' field - width = 1 - if "width" not in sig: - sig["width"] = 1 - elif not isinstance(sig["width"], int): - width, err = check_int(sig["width"], sig["name"]) - if err: - log.error("{prefix} Inter-module {inst}.{sig} 'width' " - "should be int type.".format(prefix=prefix, - inst=sig["inst_name"], - sig=sig["name"])) - error += 1 - else: - # convert to int value - sig["width"] = width + raw_width = sig.get("width", 1) + raw_width_value = raw_width + if isinstance(raw_width, Parameter): + raw_width_value = raw_width.default + + width, err = check_int(raw_width_value, sig["name"]) + if err: + log.error(f"{prefix} Inter-module {sig['inst_name']}.{sig['name']} 'width' " + "should be int type.") + error += 1 + + # We leave parameters as they are. If it's an int, use the converted value + if not isinstance(raw_width, Parameter): + sig["width"] = width # Add empty string if no explicit default for dangling pins is given. # In that case, dangling pins of type struct will be tied to the default @@ -821,20 +822,26 @@ def check_intermodule(topcfg: Dict, prefix: str) -> int: # Determine if broadcast or one-to-N log.debug("Handling inter-sig {} {}".format(req_struct['name'], total_width)) + + if isinstance(req_struct["width"], Parameter): + width = int(req_struct["width"].default) + else: + width = req_struct["width"] + req_struct["end_idx"] = -1 - if req_struct["width"] > 1 or len(rsps) != 1: + if width > 1 or len(rsps) != 1: # If req width is same to the every width of rsps ==> broadcast - if len(rsps) * [req_struct["width"]] == widths: + if len(rsps) * [width] == widths: log.debug("broadcast type") req_struct["top_type"] = "broadcast" # If req width is same as total width of rsps ==> one-to-N - elif req_struct["width"] == total_width: + elif width == total_width: log.debug("one-to-N type") req_struct["top_type"] = "one-to-N" # one-to-N connection is not fully populated - elif req_struct["width"] > total_width: + elif width > total_width: log.debug("partial one-to-N type") req_struct["top_type"] = "partial-one-to-N" req_struct["end_idx"] = len(rsps) @@ -950,8 +957,13 @@ def im_netname(sig: OrderedDict, # custom default has been specified if obj["default"]: return obj["default"] - return "{package}::{struct}_DEFAULT".format( - package=obj["package"], struct=obj["struct"].upper()) + if isinstance(sig["width"], Parameter): + return "{{{param}{{{package}::{struct}_DEFAULT}}}}".format( + param=sig["width"].name_top, package=obj["package"], + struct=obj["struct"].upper()) + else: + return "{package}::{struct}_DEFAULT".format( + package=obj["package"], struct=obj["struct"].upper()) return "" diff --git a/util/topgen/merge.py b/util/topgen/merge.py index 61ad7d6db633c..1b06ae79745cd 100644 --- a/util/topgen/merge.py +++ b/util/topgen/merge.py @@ -166,6 +166,14 @@ def elaborate_instance(instance, block: IpBlock): # to convert and copy them here. instance["inter_signal_list"] = [s.as_dict() for s in block.inter_signals] + # If we have width-parametrized intersignal, we need to update the intersignal param name + # the the instance mangled param name + for s in instance["inter_signal_list"]: + if isinstance(s['width'], Parameter): + for p in instance["param_list"]: + if p['name'] == s['width'].name: + s['width'].name_top = p['name_top'] + # An instance must either have a 'base_addr' address or a 'base_addrs' # address, but can't have both. base_addrs = instance.get('base_addrs')