seanmk
seanmk

Reputation: 1963

Is there a way to define something like a C struct in Verilog

I have a project, written in Verilog (importantly not SystemVerilog) which has gotten a little unmanageable due to the number of signals being passed along between different parts of the design. Since I have several modules which all need the same signals, I was thinking of trying to consolidate them into a smaller number of named objects. The questions is how to do it.

Let's say this is my contrived example:

module mymodule(sig_a_in, sig_b_in, sig_c_in, sig_d_in, sig_e_in,
                sig_a_out, sig_b_out, sig_c_out, sig_d_out, sig_e_out);

  input wire sig_a_in, sig_b_in, sig_c_in;
  input wire [5  : 0] sig_d_in;
  input wire [31 : 0] sig_e_in;
  output reg sig_a_out, sig_b_out, sig_c_out;
  output reg [5  : 0] sig_d_out;
  output reg [31 : 0] sig_e_out;
endmodule

It is important, in my opinion, for readability that I can reference the signals by name. However, I don't want to have to pass them all individually to each module. Again, I am only using Verilog in this project so SystemVerilog constructs are not possible.

My first thought was that I would just combine them into one bus and then use defined names to reference the individual wires. This is kind of clunky, however, particularly when you add other buses into the mix. This is pretty trivial in my contrived example because the signals have an obvious implicit order, but in real life they don't.

`define SIGNAL_BUS_WIDTH 41
`define A 0
`define B 1
`define C 2
`define D 3
`define E 9

module mymodule(signal_bus_in, signal_bus_out);

  input wire [`SIGNAL_BUS_WIDTH-1 : 0] signal_bus_in;
  output reg [`SIGNAL_BUS_WIDTH-1 : 0] signal_bus_out;

  // reference a
  signal_bus_in[`A]
  // reference d?  Not good when names don't have an obvious order
  signal_bus_in[`E-1 : `D]
endmodule

Finally, in addition to all of this, it is necessary for the tool chain to be able to distinguish which wires in the struct are being used by the module and to only synthesize those wires. Not all the modules use all the wires, so I want to avoid having extra unused paths. I think that the tools should be smart enough to do this, even in my bus example above, but I'm not completely sure.

Is there a good way to get what I want in Verilog?

Upvotes: 6

Views: 6874

Answers (3)

Jared Davis
Jared Davis

Reputation: 569

To avoid relying on the preprocessor so much, you might try using localparam and function declarations to simulate structures. Below is a mock-up of a "mybus" structure with four fields, named A through D, of various sizes, to illustrate this idea.

You could put these supporting parameters and functions into a file that you include only in the various modules that need to construct and destructure this kind of bus. This could perhaps allow you to use shorter names without fear of clashes.

module test ;


// Boilerplate structure size definitions -- you could automatically generate these
// with a simple script and put them into an include file.

localparam mybus_a_size = 4;
localparam mybus_a_offset = 0;

localparam mybus_b_size = 8;
localparam mybus_b_offset = mybus_a_offset + mybus_a_size;

localparam mybus_c_size = 4;
localparam mybus_c_offset = mybus_b_offset + mybus_b_size;

localparam mybus_d_size = 6;
localparam mybus_d_offset = mybus_c_offset + mybus_c_size;

localparam mybus_size = mybus_a_size + mybus_b_size + mybus_c_size + mybus_d_size;


// accessor functions, i.e., instead of bus.a you write mybus_a(bus)

function [mybus_a_size-1:0] mybus_a (input [mybus_size-1:0] in);
  mybus_a = in[mybus_a_size + mybus_a_offset - 1 : mybus_a_offset];
endfunction

function [mybus_b_size-1:0] mybus_b (input [mybus_size-1:0] in);
  mybus_b = in[mybus_b_size + mybus_b_offset - 1 : mybus_b_offset];
endfunction

function [mybus_c_size-1:0] mybus_c (input [mybus_size-1:0] in);
  mybus_c = in[mybus_c_size + mybus_c_offset - 1 : mybus_c_offset];
endfunction

function [mybus_d_size-1:0] mybus_d (input [mybus_size-1:0] in);
  mybus_d = in[mybus_d_size + mybus_d_offset - 1 : mybus_d_offset];
endfunction


// constructor function -- build a mybus out of its components

function [mybus_size-1:0] make_mybus(input [mybus_a_size-1:0] a,
                                      input [mybus_b_size-1:0] b,
                                      input [mybus_c_size-1:0] c,
                                      input [mybus_d_size-1:0] d);
  make_mybus = {d,c,b,a};
endfunction


// example of using this stuff

reg [mybus_size - 1 : 0] bus;

initial begin
  bus = make_mybus(1,2,3,4);
  $display("Hello, my bus is { a=%b, b=%b, c=%b, d=%b }", mybus_a(bus), mybus_b(bus), mybus_c(bus), mybus_d(bus));
end

endmodule

This mock up might make a good starting place. Some obvious improvements would be to generate all of this boilerplate automatically from a simple script, and to add additional constructor functions like "setters" in C++, i.e.,

set_mybus_a(mybus, 5)   // set mybus.a = 5

Upvotes: 1

Unn
Unn

Reputation: 5098

You can always extend you idea a bit further by use macros to define the range of each signal rather than an endpoint:

`define WIDTH 41
`define sigA 0:0
`define sigB 1:1
`define sigC 2:2
`define sigD 8:3
`define sigE 40:9

module mymodule(signal_bus_in, signal_bus_out);

  input wire [`WIDTH-1 : 0] signal_bus_in;
  output reg [`WIDTH-1 : 0] signal_bus_out;

  ...
  // signal a
  signal_bus_in[`sigA];
  // signal d
  signal_bus_in[`sigD];
  ...

Of course, this isnt as easy as SystemVerilog packed structs (which is why they exist!), but it will work for what you want. Also, this added order to your lines as well, but I see no structure that wont do that; even structs will add an order to your signals. So long as you use the macros however, it doesnt really matter that there order except when you define the marcos.

Any good synthesis tool should be able to throw out any port or wire that does not drive anything or is not drive by anything, so unless you have to explicitly tell it which lines to ignore for some reason, you shouldnt worry about it synthesizing extra space for unused module pins.

Upvotes: 3

Ari
Ari

Reputation: 7556

Verilog does not have structs. IMO combining all signals in a long vector (or as you call it a bus) is your best bet. But, you can slightly improve upon your macros:

`define SIGNAL_BUS_WIDTH 41
`define A 0:0
`define B 1:1
`define C 2:2
`define D 8:3
`define E 40:9

// reference a
signal_bus_in[`A]
// reference d  
signal_bus_in[`D]
// reference e  
signal_bus_in[`E]

Most synthesis tools do not create extra logic for the wires that are not connected and treat them as dont cares.

Upvotes: 2

Related Questions