Forums » Development »
Support for tri-state
Added by Lane Brooks over 4 years ago
I have been evaluating verilator and find the approach exactly what I want. My only issue with it is the lack of tri-state support. I see that in the TODO file it is a long-term feature. Can you provide me a status update on tristate support? Has there been any work on it yet? Is it a natural extension or does it require a lot of re-architecting?
Replies (8)
RE: tri-state - Added by Wilson Snyder over 4 years ago
Those notes are from Verilator 1.x about 5 years ago, unfortunately nothing has been done in this area. Unfortunately this is a fairly major project I don't think I have time for now, would you be interested in doing some of the work?
There's two levels of possible support, first would be tri-states that are all internal to the module being Verilated, after that would be adding pins that support tri-states. Which is interesting to you?
RE: tri-state - Added by Lane Brooks over 4 years ago
My main interest is enabling tri-state pins so that I can implement logic that talks to an external tri-state bus. I2C is the type of bus I most frequently use. I do also have interest, however, in internal tri-state to implement switch logic. I will often put custom logic in our analog blocks that typically extract and simulate in the digital domain using switches.
I would love to do the work to implement tristate but am rather limited on time as well. Can you estimate how long it would you to implement such a feature? That would help me know how big of a project it is provide me a minimum bound on how long it would take me.
RE: tri-state - Added by Wilson Snyder over 4 years ago
General scheme¶
Here's the scheme that Verilator used in version 1, and I've also used manually when I wanted to remove a few tristate signals in my designs (such as for I2C and DDR.) Others are possible, but this is simple to implement and performs well (esp compared to what most Verilog simulators do!)
For
inout [BITS] x;
Convert this into
output [BITS] x__out; // Resolved value
output [BITS] x__outen; // Resolved != Z
input [BITS] x__in; // Input
input [BITS] x__inen; // Input != Z
Then we create the output value by resolving all of the multiple drivers.
For example if there's two internal drivers plus one external one (the input statement):
assign x = x1en ? x1value : 'bz;
assign x = x2en ? x2value : 'bz;
This first gets converted to a _in & _inen unique for each drive point.
assign x1__in = x1value;
assign x1__inen = x1en;
assign x2__in = x2value;
assign x2__inen = x2en;
Then the output value resolves based on each inen
assign x__outen = x1__inen | x2__inen | x__inen;
assign x__out = ( (x1__inen & x1_in)
| (x2__inen & x2_in)
| (x__inen & x_in)
| (~x1__inen & ~x2__inen & ~x__inen & 'bX);
where the last term is what to do when there are no drivers; this value should use the --x-assign option to determine what to do; Xs is safest but 0 is fastest. Also this would be replaced by a constant if there are pullup/pulldowns on the net (and __outen would always be high).
Also any "x <= " delayed assignments need to be similarly expanded into setting a _in/_inen and a resolution stage.
The final complication is the resolution must be cross-module, so can be done only at one point in the hierarchy - presumably the top level of the Verilated module, though it's better to do it at the lowest possible module that sees all the drivers (better because if it's instantiated multiple times the logic will only occur once).
Exterior Inouts¶
For C++, expose the __in, __inen, __out, __outen signals generated above to the top level ports. The __in and __inen signals from outside feed into the resolution steps above.
For SystemC, we could use SystemC tristate types, but as we need to expand C++ types, it seems easier just to have SystemC get split just as with C++.
Tasks¶
I'd estimate this as a two week task. It's probably a week of coding, plus testing and time for you'd need to get to know the code, etc.
Write tests¶
This will help scope the problem and aid later debug. It's best to do this up front. Also as problems are found in larger tests, add new small tests to show the problems.
Test 1: Inout 64/32/16/8 bits all interior to module.
Test 2: Inout 64/32/16/8 bits crossing into top module.
Test 3: Inout functions/tasks and public func/tasks (if needed).
Major source edits¶
V3Unknown needs to be greatly extended, or a new V3Tristate step added which requests some help from V3Unknown. This will perform the __inen expansion described above.
Other source edits¶
I made this list with a grep for "AstVar", "varp" - where variables are used. Also "isIn", "isOut", "isTristate" as many probably need editing as they didn't assume tristates when written.
V3AstNodes.cpp, V3AstNodes.h
Probably some minor changes as driven by other source changes.
V3Const.cpp:
Some additional optimizations involving tristates
may be desirable, but not required.
V3EmitC.cpp:
Add any additional output rules. If follow the
__en above this should be minimal.
V3Gate.cpp:
Probably no change. May want to further optimize
tristates later here, but if convert them to __en in
V3Unknown should be ok without changes.
V3Trace.cpp:
For now, tracing a tristate could simply trace all
of the generated __in, __out, __inen and __outen
signals. Later it would be nicer to fix this trace
them as normal 4-state signals, probably another few
days effort. This will require also adding tristate
support to SystemPerl's trace functions (what
Verilator calls to create traces.)
V3LinkLValue.cpp:
Assignments to inouts need to be marked as lvalues.
V3Inline.cpp:
Need to understand how to inline modules with inout pins.
This should be very easy; inouts of the submodule can just be
substituted for the pin name of the instantiating module.
V3Inst.cpp:
Need to connect inout pins; similar code to V3Inline.
V3Link.cpp:
V3LinkResolve.cpp:
No obvious change. May need to add code to promote
some wires to tristates as discussed in the TODO.
V3Task.cpp:
Some major changes are needed to add "inout" support
for functions and tasks. For now it may be acceptable
to simply make func/task inout's be unsupported.
V3Width.cpp:
Fix width resolution of tristates (search for
isTristate) - just make concats on tristate pins
unsupported?
V3Delayed.cpp:
None - Assuming earlier step pre-converts "tristate
<= x;" to "temp_sig# <= x;" then does tristate
resolution "always @* tristate =
resolve(temp_sig1,temp_sig2...)".
No source change, or minimal¶
V3Active.cpp: None V3ActiveTop.cpp: None V3Begin.cpp: None V3Branch.cpp: None V3Cast.cpp: None V3Changed.cpp: Prob none - post _en V3Clock.cpp: Raise unsupported error if clock var is a tristate V3Combine.cpp: None V3Dead.cpp: None V3Depth.cpp: None V3DepthBlock.cpp: None V3Descope.cpp: None V3EmitV.cpp: None V3Expand.cpp: None V3GenClk.cpp: Raise unsupported error if clock var is a tristate V3Life.cpp: Prob none - post _en V3LifePost.cpp: Prob none - post _en V3LinkCells.cpp: None V3LinkDot.cpp: None V3LinkLevel.cpp: When build wrapper, make wrap pin be inout if top pin is inout V3LinkParse.cpp: None V3Localize.cpp: None V3Name.cpp: None V3Order.cpp: Assumes primary IOs are inputs or outputs, but will be after __inen, so ok V3Param.cpp: None V3Premit.cpp: None V3Scope.cpp: None V3Signed.cpp: None V3Split.cpp: None V3SplitAs.cpp: None V3Stats.cpp: None V3Subst.cpp: None V3Table.cpp: Disable table opt if any tristates - one line add V3TraceDecl.cpp: None V3Unroll.cpp: Raise unsupported error if for variable is tristate variable
RE: tri-state - Added by Lane Brooks over 4 years ago
Wow. Thanks for such a thorough responce. How does bus conflict get handled under this approach?
RE: tri-state - Added by Wilson Snyder over 4 years ago
Good point about bus conflict, I'd say if -x-assign=0 then you're accepting the hazards and just wire OR it as shown, but otherwise it should drive Xs. Furthermore it seems good to add an assertion error on bus conflict iff --assert is set.
BTW, just getting a few tests (that will fail) would be a fine start.
RE: tri-state - Added by Lane Brooks over 4 years ago
This is great information. Since I am new to verilator, I have a lot to learn before I will be able to contribute anything on this matter. I will use your method as suggested above manually for now. Perhaps after some more experience with verilator I can help contribute some of your suggestions. As I am still evaluating verilator, tristate was my biggest obstacle. It is still an obstacle, but I feel I can work around it. Now I need to take a shot at implementing behavioral C++ models for our low-level analog black boxes.
RE: tri-state - Added by Lane Brooks over 4 years ago
Wilson,
I think there is an issue with your original proposed solution. You can actually create tristates without having any inouts. For example consider a mux as follows:
assign X = (SEL==0) ? A0 : 1'bz; assign X = (SEL==1) ? A1 : 1'bz; assign X = (SEL==2) ? A2 : 1'bz;
Or equivalently, you can create the same logic in a hierarchical fashion as follows:
mux u_mux[2:0](.A({A2,A1,A0}),
.OE({SEL==2, SEL==1, SEL==0}),
.O(X));
module mux(input A, input OE, output O);
assign O = (OE) ? A : 1'bz;
endmodule
Observe this netlist has tristates but no inouts. I use this sort of structure quite frequently in custom design when designing large decoders. As such, I would propose an alternative algorithm for tristate expansion:
1. Any inout ports get expanded as __in and __out signals (no __en signals initially)
2. For any signal that drives a Z, create an __en signal as initially suggested.
3. At each level in the hierarchy, create the __en expansion logic initially suggested for any signals (VarRefs) that have multiple LV drivers and mark the __en signal as "used"
4. Any unused __en signals get passed up the hierarchy one level and the previous step is repeated.
The change can be summarized such that inouts just expand as __in and __out signals. It is the presence of a Z that triggers the creation of a __en signal. That __en signal then propigates up the hierarchy until it is terminated at the level where multiple LV varref's drive the corresponding net.
RE: tri-state - Added by Wilson Snyder over 4 years ago
I like your approach.
It also occurs to me that the Verilog PLI standard using two vectors a,b is
0 a=0 b=0
1 a=1 b=0
Z a=0 b=1
X a=1 b=1
This suggests we may want an __tri instead that's the inverse of __en. This has the advantage that loading "enable" is simply loading 0, but I think the resolution logic gets more complicated. It's easy enough to try both once there's working code and let some benchmarks decide.
(1-8/8)
![[logo]](/img/veripool_small.png)