I am working on a non-synthesizable project (https://github.com/emu-russia/dmgcpu) that makes extensive use of tri-state signals. This causes problems because projects like Verilator and Yosys don't handle those very well.
Explain how this is achieved.
Yosys already has the tribuf -logic pass, which can merge signals driven by multiple tribuf cells into a $pmux, allowing the elimination of tristate signals in a design. However, it didn't handle tristates nested in muxes.
This change implements the -propagate option, which propagates tri-state buffers through muxes.
This allows for eliminating all tristate usage in a design by flattening the module hierarchy, detecting and propagating all tribuf cells, and resolving them as a $pmux. See https://github.com/emu-russia/dmgcpu/pull/292 for this being done in practice.
If applicable, please suggest to reviewers how they can test the change.
The change includes the test file tests/techmap/tribuf.ys, which tests many of the cases the implementation handles and exemplifies how the command is used.
Summary of changes
The PR can be split into these changes:
Add an -assert option to the eval command.
Add support for evaluating and resolving tri-state signals in consteval.cc.
Add the -propagate option to the tribuf command.
Add the -force option to the tribuf command.
The main change is the -propagate option, and the two above are only used to allow automatic testing of that change. If you prefer, I can split up this PR.
eval -assert
Asserts that a signal evaluates to a specific value. This is used in conjunction with equiv_make to check if the modifications applied maintain the circuit behavior. I think most tests used miter -equiv instead, but I'm not sure if that works with designs with tristate signals (at least it didn't work before adding tri-state support to consteval).
When -table is used, it asserts the same value for each input combination. The command will first print the result or table before erroring out.
Consteval tri-state signals
This change makes consteval.cc resolve signals from multiple drivers, following the Verilog spec but ignoring signal strength.
The old implementation contained a SigMap values_map that mapped the signals to their constant value. This was split into a ConstMap that maps a sigbit to its possibly partially evaluated State, and a SigPool evaluated that tells if a sigbit was fully evaluated.
ConstEval::add handles resolving the new value assigned to a sigbit with its previous value, like resolving conflicting values to x, or keeping one value when the other is z.
Stopped using ConstEval::set internally, replacing it with ConstEval::assign. ConstEval::set is now only used externally and marks a value as evaluated.
tribuf -propagate
Propagates tri-state signals through muxes and tribuf cells. Also reworks the implementation of -merge, making it work at sigbit granularity and splitting tribuf cells when necessary.
tribuf -logic -force
By default, tribuf -logic will not replace a tribuf cell that is driving an output port. -force allows doing something like tribuf -logic -force w:OutPort %ci1 in case the output port is not actually a tristate port.
What are the reasons/motivation for this change?
I am working on a non-synthesizable project (https://github.com/emu-russia/dmgcpu) that makes extensive use of tri-state signals. This causes problems because projects like Verilator and Yosys don't handle those very well.
Explain how this is achieved.
Yosys already has the
tribuf -logic
pass, which can merge signals driven by multiple tribuf cells into a$pmux
, allowing the elimination of tristate signals in a design. However, it didn't handle tristates nested in muxes.This change implements the
-propagate
option, which propagates tri-state buffers through muxes.This allows for eliminating all tristate usage in a design by flattening the module hierarchy, detecting and propagating all tribuf cells, and resolving them as a
$pmux
. See https://github.com/emu-russia/dmgcpu/pull/292 for this being done in practice.If applicable, please suggest to reviewers how they can test the change.
The change includes the test file
tests/techmap/tribuf.ys
, which tests many of the cases the implementation handles and exemplifies how the command is used.Summary of changes
The PR can be split into these changes:
-assert
option to theeval
command.consteval.cc
.-propagate
option to thetribuf
command.-force
option to thetribuf
command.The main change is the
-propagate
option, and the two above are only used to allow automatic testing of that change. If you prefer, I can split up this PR.eval -assert
Asserts that a signal evaluates to a specific value. This is used in conjunction with
equiv_make
to check if the modifications applied maintain the circuit behavior. I think most tests usedmiter -equiv
instead, but I'm not sure if that works with designs with tristate signals (at least it didn't work before adding tri-state support to consteval).When
-table
is used, it asserts the same value for each input combination. The command will first print the result or table before erroring out.Consteval tri-state signals
This change makes
consteval.cc
resolve signals from multiple drivers, following the Verilog spec but ignoring signal strength.The old implementation contained a
SigMap values_map
that mapped the signals to their constant value. This was split into aConstMap
that maps a sigbit to its possibly partially evaluatedState
, and aSigPool evaluated
that tells if a sigbit was fully evaluated.ConstEval::add
handles resolving the new value assigned to a sigbit with its previous value, like resolving conflicting values tox
, or keeping one value when the other isz
.Stopped using
ConstEval::set
internally, replacing it withConstEval::assign
.ConstEval::set
is now only used externally and marks a value asevaluated
.tribuf -propagate
Propagates tri-state signals through muxes and tribuf cells. Also reworks the implementation of
-merge
, making it work at sigbit granularity and splitting tribuf cells when necessary.tribuf -logic -force
By default,
tribuf -logic
will not replace a tribuf cell that is driving an output port.-force
allows doing something liketribuf -logic -force w:OutPort %ci1
in case the output port is not actually a tristate port.