Forum Discussion
How to make bidirectional bus in SystemVerilog
I have a bidirectional 8 bit bus for communicating between my CPLD and my main CPU. At the top level module I declared this like this (left out details of other IO stuff):
module top ( inout [7:0] gpbus, input bit rd, input bit wr ); When I drive the gpbus from my test bench it works fine. When I set the gpbus to 8'bZ it does tri-state it. However, when I drive it from inside my module nothing changes at the test bench. It stays tri-stated. How do I correctly create and connect such a bus and simulate it? I've tried using the alias statement but no matter what I do that creates an error "near text 'alias' expecting endmodule".11 Replies
- Altera_Forum
Honored Contributor
Most likely a connection error, but you did not show enough code that might give a hint at what is wrong. How are you driving the bus in your testbench and CPLD? You need to be using a continuous assignment in both places. Either
assign gpbus = wr ? data : 8'z; or assign gpbus = data; And you procedurally assign data to 8'bz when that side is not driving. - Altera_Forum
Honored Contributor
Here is what I am trying to do (simplified and with extraneous fluff removed). Basically I have a multiplexed 8 bit address/data bus. I want to latch an address (0 or 1) and then write data to some output bits depending on the address (this seems to work just fine in the test bench when I simulate in Modelsim). Then I want to read the state of some input bits (this is the part that I can never see on the bidirectional bus in the simulator).
In the simulator my cpubus (in the testbench) always appears to be driven by the values in test bench, even if that is a Z value. How do I get it to show the value that is read back from the device? my device toplevel: module top ( inout wire [7:0] cpldbus, input bit ale, input bit rd, input bit wr, output reg [15:0] outbit, input reg [15:0] inbit ); logic [7:0] addr; logic [7:0] data; always_ff @ (posedge ale, posedge wr) begin if (ale) addr = cpldbus; // when ale is true latch the address if (wr) begin if (addr == 0) outbit[7:0] = cpldbus; if (addr == 1) outbit[15:8] = cpldbus; end end assign cpldbus = (rd) ? data : 8'bZ; always_ff @ (posedge rd) begin if (addr == 2) data = inbit[7:0]; if (addr == 3) data = inbit[15:8]; end endmodule Here is the test bench: module test (); localparam time PERIOD = (1.0e9/14.7456e6)*1ns; logic [7:0] cpubus; bit ale; bit rd; bit wr; reg [15:0] outbit; reg [15:0] inbit; logic [7:0] data; // DUT top u1( .cpldbus(cpubus[7:0]), .ale, .rd, .wr, .outbit, .inbit ); initial begin // put some dummy data on inbit inbit = 8'h33; // Stimulus $stop; // to allow signal setup on wave page // Write A5 to addr 0 # PERIOD data = 0; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD data = 8'hA5; # PERIOD wr = 1; # PERIOD wr = 0; // Write 5A to addr 1 # PERIOD data = 1; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD data = 8'h5A; # PERIOD wr = 1; # PERIOD wr = 0; // Read addr 2 # PERIOD data = 2; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD rd = 1; # PERIOD rd = 0; end assign cpubus = (ale || wr) ? data : 8'bZ; endmodule Well, maybe this is working and I'm not supposed to see cpubus in the testbench take on the value of cpldbus in my device module. Am I correct in that? I do see the cpldbus of top driving out that h33 that I forced onto inbit. But at that time cpubus of the test module is showing tri-state. - Altera_Forum
Honored Contributor
It would help if you attached the example code you are trying to model, what you wrote would not even compile.
You need to make sure the testbench is driving Z's when you want to read what the device is driving, and the device must be driving Z's to read what the testbench is driving. Dave - Altera_Forum
Honored Contributor
--- Quote Start --- It would help if you attached the example code you are trying to model, what you wrote would not even compile. You need to make sure the testbench is driving Z's when you want to read what the device is driving, and the device must be driving Z's to read what the testbench is driving. Dave --- Quote End --- OK, I have made a small test case of actual code and I put that in the previous response. It messed up all my indenting here so its a bit ugly to see the structure. But it compiles on my system and runs. I have renamed the buses in the test bench to cpubus and the bus in the top module is now cpldbus so that its easier to discuss. Is it true that I will not see the cpubus in the testbench take on the actual value drive up from the top module? - Altera_Forum
Honored Contributor
No.
When only the top module is driving, you should see the driven value. - Altera_Forum
Honored Contributor
I only see the value from the lower module at the cpldbus. That value never drives the cpubus. At the time when I see cpldbus driving '8h33 the cpubus is showing tristate, which is the "value" it gets from the assign statement when both ale and wr are false.
Is there something else one needs to do to implement a bidirectional bus? - Altera_Forum
Honored Contributor
Your code is not legal. Apparently the simulator version you are using is not catching the error and producing incorrect results. You cannot connect a variable to an inout port, only wires are allowed to be connected to inout ports. Change the declaration of cpubus in module test to
Then cpubus will always show the resolved value. Also, there may be a race in your testbench because you change wr and cpubus simultaneously. You should be a delay in your continuous assignments. P.S. You can wrap your code withwire cpubus;
and your indentation will be preserved.your codemodule top ( inout wire cpldbus, input bit ale, input bit rd, input bit wr, output reg outbit, input reg inbit ); logic addr; logic data; always_ff @ (posedge ale, posedge wr) begin if (ale) addr = cpldbus; // when ale is true latch the address if (wr) begin if (addr == 0) outbit = cpldbus; if (addr == 1) outbit = cpldbus; end end assign# 1ps cpldbus = (rd) ? data : 8'bZ; always_ff @ (posedge rd) begin if (addr == 2) data = inbit; if (addr == 3) data = inbit; end endmodule module test (); localparam time PERIOD = (1.0e9/14.7456e6)*1ns; wire cpubus; bit ale; bit rd; bit wr; reg outbit; reg inbit; logic data; // DUT top u1( .cpldbus(cpubus), .ale, .rd, .wr, .outbit, .inbit ); initial begin // put some dummy data on inbit inbit = 8'h33; // Stimulus //$stop; // to allow signal setup on wave page // Write A5 to addr 0 # PERIOD data = 0; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD data = 8'hA5; # PERIOD wr = 1; # PERIOD wr = 0; // Write 5A to addr 1 # PERIOD data = 1; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD data = 8'h5A; # PERIOD wr = 1; # PERIOD wr = 0; // Read addr 2 # PERIOD data = 2; # PERIOD ale = 1; # PERIOD ale = 0; # PERIOD rd = 1; # PERIOD rd = 0; end assign# 1ps cpubus = (ale || wr) ? data : 8'bZ; endmodule - Altera_Forum
Honored Contributor
Thanks dave_59. Your pointers worked. Also the delays to solve the race condition is great. But that only affects the simulation, not the actual hardware that is created, right? Of course it is important to simulate what will really happen in the hardware.
I appreciate the help a lot since I'm really new to this and have to get a design finished this week! One more question if I may. In order to make things work with the delays in there I have to change the way the write signal is used. I can either used the negedge (which will wait until long after the cpldbus is stable), or I could use an always_latch construct which will continuously follow the value of cpldbus until wr goes false. The only problem with doing that is you might get some unknown value from the cpldbus until it has stabilized (after the delay). Its probably better to use the negedge idea so that we have synchronous changes and never have some unknown data going through. Style-wise I wonder if its better to write separate processes for the address latch and the wr operation, like this (seems clearer to me):always_ff @ (negedge ale) addr = cpldbus; always_ff @ (negedge wr) begin if (addr == 0) outbit = cpldbus; if (addr == 1) outbit = cpldbus; end - Altera_Forum
Honored Contributor
You probably want to use always_latch as that is closer to the actual behavior. It's OK if cpldbus has not stabilized, the block will continue to execute passing the value on to outbit.
- Altera_Forum
Honored Contributor
--- Quote Start --- You probably want to use always_latch as that is closer to the actual behavior. It's OK if cpldbus has not stabilized, the block will continue to execute passing the value on to outbit. --- Quote End --- Now that confuses me a bit. I thought that each of those constructs would create different hardware. always_latch would create level sensitive gates and always_ff would create edge sensitive gates. Am I way off base there?