From f42923d8961a73d66ec0fec3cdddcc9e444d205c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 21 Jul 2026 17:45:02 +0200 Subject: [PATCH] mul2dsp: declare $__mul and $__soft_mul so their ports have directions Neither is a cell anything implements. They exist so a mapping rule can re-emit a multiplier without matching itself, and a later techmap or `chtype` consumes them. Nothing ever declared them, so `Cell::port_dir()` fell through to PD_UNKNOWN for every port, and anything that has to tell a cell's inputs from its outputs had to guess. signorm guesses "output": setup_driven_wires() skips only PD_INPUT, so \A and \B of a $__mul are read as drivers. \B is the enclosing module's own input, already driven by its $input_port cell, and \A is a slice, so both take the fallback -- interpose a helper wire and connect it -- and both sides then look driven, which is what a $connect means. _80_mul's wide branch has two $__mul instances, and it runs `proc; clean` through _TECHMAP_DO_, whose closing opt_expr normalizes the map design. Result: four helper wires and four $connect cells manufactured out of a design that has no multi-driver net in it anywhere. Declaring them as blackboxes gives the interface a definition without making them mappable: techmap skips blackbox templates outright (`tpl->get_blackbox_attribute(ignore_wb)`), and every rule that emits these types carries an explicit techmap_celltype, so nothing was relying on matching them by module name. 96deee136 stopped the $connect cells escaping the index, which is right regardless; this stops them being created. `make -C tests` is unchanged at 7 failures. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MQU5XCqDYY8MbivGkNRrDo --- techlibs/common/mul2dsp.v | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v index 4ac03cad4..e5eb8eb4f 100644 --- a/techlibs/common/mul2dsp.v +++ b/techlibs/common/mul2dsp.v @@ -49,6 +49,51 @@ $fatal(1, "Macro DSP_NAME must be defined"); `define MAX(a,b) (a > b ? a : b) `define MIN(a,b) (a < b ? a : b) +// $__mul and $__soft_mul are not cells anything implements: the rules below +// re-emit a multiplier as one of them so that the match does not recurse into +// itself, and a later techmap or `chtype` consumes them. Nothing declared them, +// so port_dir() answered PD_UNKNOWN for their ports, and anything that has to +// tell a cell's inputs from its outputs then has to guess -- the signorm index +// guesses "output", reads A and B as drivers, and manufactures a multi-driver +// conflict on every net feeding one. Give the interface a definition instead. +// +// The blackbox attribute is what keeps techmap from taking these as mapping +// templates: it skips blackbox templates outright, so they stay pure +// declarations. Every rule that produces them carries an explicit +// techmap_celltype, so nothing depends on matching by module name here. + +(* blackbox *) +module \$__mul (A, B, Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + (* force_downto *) + input [A_WIDTH-1:0] A; + (* force_downto *) + input [B_WIDTH-1:0] B; + (* force_downto *) + output [Y_WIDTH-1:0] Y; +endmodule + +(* blackbox *) +module \$__soft_mul (A, B, Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + (* force_downto *) + input [A_WIDTH-1:0] A; + (* force_downto *) + input [B_WIDTH-1:0] B; + (* force_downto *) + output [Y_WIDTH-1:0] Y; +endmodule + (* techmap_celltype = "$mul $__mul" *) module _80_mul (A, B, Y); parameter A_SIGNED = 0;