Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
21587 Discussions

How to make bidirectional bus in SystemVerilog

Altera_Forum
Honored Contributor II
8,391 Views

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".
0 Kudos
11 Replies
Altera_Forum
Honored Contributor II
5,592 Views

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.
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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.
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

 

--- 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?
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

No. 

When only the top module is driving, you should see the driven value.
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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?
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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 

 

wire cpubus;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 with  

your code and your indentation will be preserved. 

 

module 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
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

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.

0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

 

--- 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?
0 Kudos
Altera_Forum
Honored Contributor II
5,592 Views

No, you are not off base. I don't know your design intent. If the real hardware depends on delays to operate, you need to account for it in your model, either with proper delays, or choosing the correct abstraction.

0 Kudos
Reply