Commit 4b3966aa by Zachary Snow

added hdl-examples tests

parent 6cc4654a
.PHONY: all sv2v clean
.PHONY: all sv2v clean test
all: sv2v
......@@ -9,3 +9,6 @@ sv2v:
clean:
stack clean
rm -rf bin
test:
(cd test/relong; ./run.sh)
# relong Tests
These tests are borrowed from Reid Long's [HDL Examples
repository](https://bitbucket.org/ReidLong/hdl-examples). That repository was
intended to provide examples for how the conversions in this project could be
done.
The `inline_concat` files were modified to remove a stray trailing semicolon.
Each test case (say, "foo") is comprised of the following files:
1. `foo.sv`: original SystemVerilog
2. `foo.v`: hand-converted Verilog
3. `foo_tb.v`: basic testbench exercising the given modules
The SystemVerilog source file is converted to Verilog using sv2v, and then both
the converted file and the reference Verilog are simulated using Icarus Verilog.
This produces VCD files for each which are expected to match exactly, except for
the timestamp.
`default_nettype none
module ALU(
input logic [2:0] operation,
input logic [31:0] left, right,
output logic [31:0] result
);
always_comb begin
result = 32'b0;
case(operation)
3'd0: begin
// Right logical shift
// Only need the lowest 5 bits for 32 bit input
result = $unsigned(left) >> right[4:0];
end
3'd1: begin
// Right arithmetic shift
result = $signed(left) >>> right[4:0];
end
3'd2: begin
// Signed Comparison
result = $signed(left) < $signed(right);
end
3'd3: begin
// Unsigned comparison
result = $unsigned(left) < $unsigned(right);
end
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
module ALU(
input wire [2:0] operation,
input wire [31:0] left, right,
output reg [31:0] result
);
// 1. Unsigned should be a no-op so simply remove those function calls
// 2. Signed modifiers don't appear to be supported on VTR, so they might need to be expanded with logic for actually doing the signed operation.
// 3. Likewise, the triple shift (arithmetic shift) operator doesn't appear to be supported in VTR so it also might need to be expanded manually.
always @* begin
result = 32'b0;
case(operation)
3'd0: begin
// Right logical shift
// Only need the lowest 5 bits for 32 bit input
result = left >> right[4:0];
end
3'd1: begin
// Right arithmetic shift
result = $signed(left) >>> right[4:0];
end
3'd2: begin
// Signed Comparison
result = $signed(left) < $signed(right);
end
3'd3: begin
// Unsigned comparison
result = left < right;
end
endcase
end
// Extra stuff for example
reg [31:0] result2;
reg [31:0] __temp;
// sameResult: assert property (result == result2);
always @* begin
result2 = 32'b0;
__temp = 32'b0;
case(operation)
3'd0: begin
// Right logical shift
// Only need the lowest 5 bits for 32 bit input
result2 = left >> right[4:0];
end
3'd1: begin
// Right arithmetic shift
// 1. Take the MSB and replicate it to form a mask
// 2. Create a mask for the bits that the shift will set correctly and mask them off of the MSB replication (sets 32 - shift bits correctly)
// 3. Combine the shift part and the masked part
__temp = {32{left[31]}} & ~ ((33'd1 << (6'd32 - right[4:0])) - 33'd1);
result2 = __temp | (left >> right[4:0]);
end
3'd2: begin
// Signed Comparison
case({left[31], right[31]})
2'b00: result2 = left < right; // Both positive
2'b01: result2 = 32'd0; // + < - (always false)
2'b10: result2 = 32'd1; // - < + (always true)
2'b11: result2 = -left > -right; // Both negative
endcase
end
3'd3: begin
// Unsigned comparison
result2 = left < right;
end
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg [2:0] operation;
reg [31:0] left, right;
wire [31:0] result;
ALU dut(
.operation(operation),
.left(left),
.right(right),
.result(result)
);
initial begin
$monitor($time, " %h %d %h = %h", left, operation, right, result);
operation = 3'd0;
left = 32'h0;
right = 32'h0;
#10;
left = 32'h80000000;
right = 32'd16;
#10;
operation = 3'd1;
#10;
left = 32'h7fff0003;
right = 32'd1;
#10;
right = 32'd0;
#10;
right = 32'd8;
#10;
operation = 3'd2;
left = 32'hffffffff;
right = 32'h10;
#10;
left = 32'h1;
right = 32'h10;
#10;
left = 32'h10;
right = 32'hffffffff;
#10;
operation = 3'd3;
left = 32'h80000000;
right = 32'h7fffffff;
#10;
left = 32'hff;
right = 32'h80000000;
#10;
operation = 3'd2;
left = 32'd10;
right = 32'd20;
#10;
left = 32'd20;
right = 32'd10;
#10;
left = 32'hffffffff; // -1
right = 32'hfffff000;
#10;
left = 32'hfffff000;
right = 32'hffffffff;
#10;
left = 32'hfffff000;
right = 32'h10;
#10;
left = 32'h10;
right = 32'hfffff000;
#10;
// operation = 3'd1;
// // for(left = 32'b0; left < 32'hffffffff; left = left + 32'b1)
// // for(right = 32'b0; right <= 32'd31; right = right + 32'b1)
// // #10 if(result != dut.result2) $error("Bad match: %h %h", result, dut.result2);
// // #10;
// left = 32'hffffffff;
// for(right = 32'b0; right <= 32'd31; right = right + 32'd1)
// #10 if(result != dut.result2) $error("Bad match: %h %h", result, dut.result2);
// #10;
$finish;
end
endmodule
`default_nettype none
module ArrayOrReduction #(
parameter SIZE = 16, WIDTH=32) (
input logic [SIZE-1:0][WIDTH-1:0] inputs,
output logic [WIDTH-1:0] result
);
// Recursively generate a pair-wise reduction
generate
if(SIZE <= 0) begin : error_case
DoesNotExit foo();
end else if (SIZE == 1) begin : base_case_1
assign result = inputs[0];
end else if(SIZE == 2) begin : base_case_2
assign result = inputs[0] | inputs[1];
end else begin : recursive_case
logic [1:0][WIDTH-1:0] subResults;
ArrayOrReduction #(.WIDTH(WIDTH), .SIZE(SIZE/2)) top(.inputs(inputs[SIZE-1:SIZE/2]), .result(subResults[1]));
ArrayOrReduction #(.WIDTH(WIDTH), .SIZE(SIZE/2)) bot(.inputs(inputs[SIZE/2-1:0]), .result(subResults[0]));
assign result = subResults[0] | subResults[1];
end
endgenerate
endmodule
module Array #(parameter ELEMENTS=16, WIDTH=32)(
// $clog2 is a built in function that does the natural log of 2
input logic [$clog2(ELEMENTS)-1:0] index,
input logic [WIDTH-1:0] element,
output logic [ELEMENTS-1:0][WIDTH-1:0] array,
input logic clock, clear, enable
);
localparam ZERO_ELEMENT = {WIDTH{1'b0}};
always_ff @(posedge clock)
if(clear)
array <= {ELEMENTS{ZERO_ELEMENT}};
else if(enable)
array[index] <= element;
endmodule
\ No newline at end of file
`default_nettype none
module ArrayOrReduction #(
parameter SIZE = 16, WIDTH=32) (
// Flattened array
input wire [(SIZE*WIDTH)-1:0] inputs,
output wire [WIDTH-1:0] result
);
// One option is to modify the code to use the flattened array, but probably
// an easier option is to just insert a generate block to unflatten
// everything.
wire [WIDTH-1:0] __inputs[SIZE-1:0];
genvar index;
generate
for(index = 0; index < SIZE; index = index + 32'd1) begin : unflatten
localparam START_BIT = index * WIDTH;
assign __inputs[index] = inputs[START_BIT + WIDTH - 1:START_BIT];
end
endgenerate
// Recursively generate a pair-wise reduction
generate
if(SIZE <= 0) begin : error_case
DoesNotExit foo();
end else if (SIZE == 1) begin : base_case_1
assign result = __inputs[0];
end else if(SIZE == 2) begin : base_case_2
assign result = __inputs[0] | __inputs[1];
end else begin : recursive_case
wire [WIDTH-1:0] subResults [1:0];
// The array needs to be re-flattened here or alternatively, just use computation on the original array
ArrayOrReduction #(.WIDTH(WIDTH), .SIZE(SIZE/2)) top(.inputs(inputs[(SIZE*WIDTH)-1:(SIZE*WIDTH)/2]), .result(subResults[1]));
ArrayOrReduction #(.WIDTH(WIDTH), .SIZE(SIZE/2)) bot(.inputs(inputs[(SIZE * WIDTH)/2-1:0]), .result(subResults[0]));
assign result = subResults[0] | subResults[1];
end
endgenerate
endmodule
module Array #(parameter ELEMENTS=16, WIDTH=32)(
input wire [clog2(ELEMENTS)-1:0] index,
input wire [WIDTH-1:0] element,
// Flattened array
output wire [(ELEMENTS*WIDTH)-1:0] array,
input wire clock, clear, enable
);
// Manually implemented clog2 (which the toolchain could in theory just copy
// into the source where-ever the build-in $clog2 is referenced)
// Verilog functions are super gross. This is defining a function called
// clog2 which takes a 32-bit input value.
function integer clog2;
input [31:0] value;
begin
value = value - 1;
// The return value is simply a variable with the same name as the
// function and the last value written to that variable is the return
// value.
for (clog2 = 0; value > 0; clog2 = clog2 + 1) begin
value = value >> 1;
end
end
endfunction
reg [WIDTH-1:0] __array[ELEMENTS-1:0];
genvar g_index;
generate
for(g_index = 0; g_index < ELEMENTS; g_index = g_index + 32'd1) begin : unflatten
localparam START_BIT = g_index * WIDTH;
assign array[START_BIT + WIDTH - 1:START_BIT] = __array[g_index];
end
endgenerate
localparam ZERO_ELEMENT = {WIDTH{1'b0}};
// I think this might synthesize correctly, but it is pretty gross.
integer reset_index;
always @(posedge clock)
if(clear) begin
for(reset_index = 0; reset_index < ELEMENTS; reset_index = reset_index + 32'd1)
__array[reset_index] <= {ZERO_ELEMENT};
end else if(enable)
__array[index] <= element;
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg [1:0] index;
reg [7:0] element;
wire [31:0] arrayData;
reg enable, clock, clear;
Array #(.ELEMENTS(4), .WIDTH(8)) arrayInstance(
.index(index),
.element(element),
.array(arrayData),
.clock(clock),
.clear(clear),
.enable(enable)
);
wire [7:0] result;
ArrayOrReduction #(.SIZE(4), .WIDTH(8)) reduction(
.inputs(arrayData),
.result(result)
);
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time, " arrayData: %h result: %h", arrayData, result);
clear = 1'b1;
index = 2'b0;
element = 8'h0;
enable = 1'b0;
repeat(3) @(posedge clock);
clear = 1'b0;
element = 8'haa;
enable = 1'b1;
@(posedge clock);
element = 8'h11;
index = 2'd1;
@(posedge clock);
element = 8'h72;
index = 2'd2;
@(posedge clock);
element = 8'h88;
index = 3'd3;
@(posedge clock);
element = 8'hff;
index = 3'd0;
enable = 1'b0;
@(posedge clock);
element = 8'h00;
enable = 1'b1;
@(posedge clock);
element = 8'h00;
index = 3'd1;
@(posedge clock);
enable = 1'b0;
index = 3'd2;
@(posedge clock);
enable = 1'b1;
index = 3'd3;
@(posedge clock);
enable = 1'b0;
repeat(5) @(posedge clock);
$finish;
end
endmodule
`default_nettype none
module Example(
input logic [1:0] select,
// This is an array of 3 (4-bit wide) elements
output logic [2:0][3:0] data
);
UniqueCase case0(.select, .data(data[0]));
WildcardCase case1(.select, .data(data[1]));
DefaultCase case2(.select, .data(data[2]));
endmodule
module UniqueCase(
input logic [1:0] select,
output logic [3:0] data
);
always_comb begin
data = 4'b0;
unique case(select)
2'd0: data = 4'ha;
2'd1: data = 4'h6;
2'd2: data = 4'h3;
/* unique means that the toolchain can assume 2'd3 will never happen */
endcase
end
endmodule
module WildcardCase(
input logic [1:0] select,
output logic [3:0] data
);
always_comb begin
data = 4'b0;
unique casez(select)
2'b00: data = 4'h3;
2'b1?: data = 4'hd;
// Unique means that the toolchain can assume 2'b01 will never happen
endcase
end
endmodule
module DefaultCase(
input logic [1:0] select,
output logic [3:0] data
);
always_comb begin
data = 4'b0;
case (select)
2'b00: data = 4'h7;
2'b01: data = 4'h9;
default: data = 4'h8;
endcase
end
endmodule
// There is also a casex construct, but it is very dangerous and not recommend to be used in real designs.
// https://www.verilogpro.com/verilog-case-casez-casex/
\ No newline at end of file
`default_nettype none
module Example(
input wire [1:0] select,
// This is an array of 3 (4-bit wide) elements
output wire [11:0] data
);
// Unflatten the array
wire [3:0] __data[2:0];
assign data = {__data[2], __data[1], __data[0]};
UniqueCase case0(.select(select), .data(__data[0]));
WildcardCase case1(.select(select), .data(__data[1]));
DefaultCase case2(.select(select), .data(__data[2]));
endmodule
module UniqueCase(
input wire [1:0] select,
output reg [3:0] data
);
always @* begin
data = 4'b0;
// Unique keyword doesn't exist in Verilog
case(select)
2'd0: data = 4'ha;
2'd1: data = 4'h6;
2'd2: data = 4'h3;
endcase
end
endmodule
module WildcardCase(
input wire [1:0] select,
output reg [3:0] data
);
always @* begin
data = 4'b0;
// Unique keyword doesn't exist in Verilog
// casez doesn't exist in VTR, so manually elaborating it
case(select) // casez
2'b00: data = 4'h3;
// 2'b1?: data = 4'hd;
2'b10: data = 4'hd;
2'b11: data = 4'hd;
endcase
end
endmodule
module DefaultCase(
input wire [1:0] select,
output reg [3:0] data
);
always @* begin
data = 4'b0;
case (select)
2'b00: data = 4'h7;
2'b01: data = 4'h9;
default: data = 4'h8;
endcase
end
endmodule
`default_nettype none
module top;
reg [1:0] select;
// This is actually a 3x4-bit array, but must be flattened for Verilog
wire [11:0] data;
Example dut(
.select(select),
.data(data)
);
reg [2:0] i; // This needs to be wider than select
initial begin
$monitor($time, " %d = {%h}", select, data);
select = 2'd0;
for(i = 0; i <= 2'd3; i = i + 3'd1) begin
#10 select = i; // Drop upper bits
end
#10 $finish;
end
endmodule
`default_nettype none
module Device(
input logic clock, clear,
output logic [3:0] data
);
SharedMemory memory(
.clock1(clock),
.clock2(clock),
.clear,
.data1(data[1:0]),
.data2(data[3:2])
);
endmodule
module SharedMemory(
input logic clock1, clock2, clear,
output logic [1:0] data1, data2
);
logic [3:0] memory;
// Just a dumb example to generate interesting values
always_ff @(posedge clock1) begin
if(clear)
memory <= 4'b0;
else
memory <= {~memory[2:0], 1'b0};
end
always_ff @(posedge clock1)
data1 <= memory[1:0];
// Technically this is pretty dangerous since it is being generated on
// clock1 domain so we would need a bunch of other logic to make sure this
// is safe, but for the example we'll just ignore that stuff.
always_ff @(posedge clock2) begin
data2 <= memory[3:2];
end
endmodule
\ No newline at end of file
`default_nettype none
module Device(
input wire clock, clear,
output wire [3:0] data
);
SharedMemory memory(
.clock1(clock),
.clock2(clock),
.clear(clear), // Verilog doesn't support inferred ports
.data1(data[1:0]),
.data2(data[3:2])
);
endmodule
module SharedMemory(
input wire clock1, clock2, clear,
output reg [1:0] data1, data2
);
reg [3:0] memory;
// Just a dumb example to generate interesting values
always @(posedge clock1) begin
if(clear)
memory <= 4'b0;
else
memory <= {~memory[2:0], 1'b0};
end
always @(posedge clock1)
data1 <= memory[1:0];
always @(posedge clock2) begin
data2 <= memory[3:2];
end
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg clock, clear;
wire [3:0] data;
Device dut(
.clock(clock),
.clear(clear),
.data(data)
);
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time, " data: %h", data);
clear = 1'b1;
repeat(3) @(posedge clock);
clear = 1'b0;
repeat(20) @(posedge clock);
$finish;
end
endmodule
`default_nettype none
// Technically the value assignment could be anything, but most tools default to a 32-bit logic assigning MODE_A = 0 and MODE_B = 1
typedef enum {MODE_A, MODE_B} Mode_t;
typedef enum logic [1:0] {READ=2'd1, WRITE=2'd2, NONE=2'd0} Operation_t;
module Example(
input logic rawMode,
output logic [1:0] rawOperation
);
Mode_t mode;
Operation_t operation;
// cast into a strongly typed variant
assign mode = Mode_t'(rawMode);
assign rawOperation = operation;
always_comb begin
case(mode)
MODE_A: operation = READ;
MODE_B: operation = WRITE;
default: operation = NONE;
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
// So Verilog is very sad when it comes to enumerations. The easiest way to emulate this behavior is probably to define a macro for the enumeration and then instantiate it everywhere the type is used.
`define MODE_T\
parameter MODE_A = 32'b0;\
parameter MODE_B = 32'b1
`define OPERATION_T\
parameter READ = 2'd1;\
parameter WRITE = 2'd2;\
parameter NONE = 2'd0
module Example(
input wire rawMode,
output wire [1:0] rawOperation
);
`MODE_T;
`OPERATION_T;
// Enumeration variables don't have types, so they need to be default wires.
// Technically this could be just one bit if the tool is smart enough to realize.
wire [31:0] mode;
reg [1:0] operation;
// No need for a cast since everything is a wire
assign mode = rawMode;
assign rawOperation = operation;
always @* begin
case(mode)
MODE_A: operation = READ;
MODE_B: operation = WRITE;
default: operation = NONE;
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg rawMode;
wire [1:0] rawOperation;
Example dut(
.rawMode(rawMode),
.rawOperation(rawOperation)
);
initial begin
$monitor($time, " rawMode: %b rawOperation: %b", rawMode, rawOperation);
rawMode = 1'b0;
#10 rawMode = 1'b1;
#10 rawMode = 1'b0;
#10 $finish;
end
endmodule
`default_nettype none
module FSM(
input logic a,
output logic x,
input logic clock, clear
);
enum {S_A, S_B, S_C} currentState, nextState;
always_ff @(posedge clock)
if(clear) begin
currentState <= S_A;
end else begin
currentState <= nextState;
end
always_comb begin
nextState = currentState;
unique case(currentState)
S_A: nextState = a ? S_B : S_C;
S_B: nextState = a ? S_A : S_B;
S_C: nextState = S_A;
endcase
end
always_comb begin
x = 1'b0;
unique case(currentState)
S_A: x = ~a;
S_B: x = 1'b1;
S_C: x = 1'b0;
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
module FSM(
input wire a,
output reg x,
input wire clock, clear
);
parameter S_A = 32'd0, S_B = 32'd1, S_C = 32'd2;
reg [31:0] currentState, nextState;
always @(posedge clock)
if(clear) begin
currentState <= S_A;
end else begin
currentState <= nextState;
end
always @* begin
nextState = currentState;
case(currentState)
S_A: nextState = a ? S_B : S_C;
S_B: nextState = a ? S_A : S_B;
S_C: nextState = S_A;
endcase
end
always @* begin
x = 1'b0;
case(currentState)
S_A: x = ~a;
S_B: x = 1'b1;
S_C: x = 1'b0;
endcase
end
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg clock, clear;
reg a;
wire x;
FSM dut(
.clock(clock),
.clear(clear),
.a(a),
.x(x)
);
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time, " a: %b x: %b state: %h", a, x, dut.currentState);
clear = 1'b1;
a = 1'b0;
repeat(3) @(posedge clock);
clear = 1'b0;
a = 1'b1;
repeat(5) @(posedge clock);
a = 1'b0;
repeat(5) @(posedge clock);
a = 1'b1;
@(posedge clock);
a = 1'b0;
@(posedge clock);
a = 1'b1;
@(posedge clock);
a = 1'b1;
@(posedge clock);
a = 1'b0;
@(posedge clock);
$finish;
end
endmodule
`default_nettype none
typedef enum {FOO, BAR} State_t;
typedef struct packed {
State_t state;
logic [31:0] data;
} MyStruct_t;
module Example(
input logic clock, clear,
input logic [7:0] dataIn,
output logic check1, check2,
output logic [63:0] checkData
);
// The automatic keyword here is super confusing, but generally does what
// you expect a C function to do. Sadly VTR doesn't support the automatic
// keyword.
function automatic State_t swapState(State_t state);
// The state variable is not shadowing the state variable in the global
// function since the function is declared above the state variable
// (Declaring functions at the start of the module before module scope
// variables is a good practice to avoid accidentally using a module scope
// in a function)
unique case(state)
FOO: return BAR;
BAR: return FOO;
endcase
endfunction : swapState
State_t state;
always_ff @(posedge clock)
if(clear)
state <= FOO;
else
state <= swapState(state);
logic [15:0] magicToken;
assign magicToken = 16'habcd;
function automatic MyStruct_t packStruct(State_t state, logic [7:0] data);
// Something interesting about system verilog is that all local variable
// declarations must be first in the function (at least for VCS to
// compile it)
logic [31:0] fullData;
State_t nextState;
// System Verilog functions can also access "local" variables from the
// module scope. This means that any variable with the same name in the
// function as something else defined in the module is shadowing the
// module variable (e.g. state in this function)
fullData = {~data, data, magicToken};
nextState = swapState(state);
return '{
state: nextState,
data: fullData
};
endfunction
MyStruct_t myStruct;
assign myStruct = packStruct(state, dataIn);
function automatic logic doCheck(MyStruct_t inputStruct);
return inputStruct.state == FOO;
endfunction : doCheck
assign check1 = doCheck(myStruct);
MyStruct_t myStruct2;
always_comb begin
myStruct2 = packStruct(swapState(state), ~dataIn);
check2 = doCheck(myStruct2);
end
assign checkData = {myStruct.data, myStruct2.data};
endmodule
\ No newline at end of file
`default_nettype none
// Enum's default to 32-bit, but this is a tool specific implementation detail
`define STATE_T\
parameter FOO = 32'd0, BAR = 32'd1;
// As an alternative to the macro based structs, they could also simply be computed
// typedef struct packed {
// State_t state; // [63:32]
// logic [31:0] data; // [31:0]
// } MyStruct_t;
module Example(
input wire clock, clear,
input wire [7:0] dataIn,
output wire check1,
output reg check2, // Split since check2 is used in an always_comb block
output wire [63:0] checkData
);
`STATE_T
// The automatic keyword here is super confusing, but generally does what
// you expect a C function to do. Sadly VTR doesn't support the automatic
// keyword.
function [31:0] swapState(input [31:0] state);
case(state)
// To return from a function assign the function name with a variable
FOO: swapState = BAR;
BAR: swapState = FOO;
endcase
// Scope ending labels are not supported in Verilog and only provide
// human readability benifits (plus compiler warnings if they are
// incorrect)
endfunction
reg [31:0] state;
always @(posedge clock)
if(clear)
state <= FOO;
else
state <= swapState(state);
wire [15:0] magicToken;
assign magicToken = 16'habcd;
function [63:0] packStruct(input [31:0] state, input [7:0] data);
// Something interesting about system verilog is that all local variable
// declarations must be first in the function (at least for VCS to
// compile it)
reg [31:0] fullData;
reg [31:0] nextState;
begin
fullData = {~data, data, magicToken};
nextState = swapState(state);
packStruct = {nextState, fullData};
end
endfunction
wire [63:0] myStruct;
assign myStruct = packStruct(state, dataIn);
function [0:0] doCheck(input [63:0] inputStruct);
doCheck = inputStruct[63:32] == FOO; // inputStruct.state
endfunction // : doCheck
assign check1 = doCheck(myStruct);
reg [63:0] myStruct2;
always @* begin
myStruct2 = packStruct(swapState(state), ~dataIn);
check2 = doCheck(myStruct2);
end
assign checkData = {myStruct[31:0], myStruct2[31:0]}; // *.data
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg clock, clear;
reg [7:0] dataIn;
wire check1, check2;
wire [63:0] checkData;
Example dut(
.clock(clock),
.clear(clear),
.dataIn(dataIn),
.check1(check1),
.check2(check2),
.checkData(checkData)
);
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time, " data: %h check: %b checkData: %h", dataIn, {check1, check2}, checkData);
clear = 1'b1;
dataIn = 8'h0;
repeat(3) @(posedge clock);
clear = 1'b0;
@(posedge clock);
dataIn = 8'haa;
repeat(20) @(posedge clock);
$finish;
end
endmodule
`default_nettype none
module Device(
input logic [7:0] a, b,
output logic [7:0] result
);
logic [7:0] result1, result2;
OrParts helper1(.data({a, b}), .result(result1));
logic [15:0] bothInputs;
assign bothInputs = {a, b};
OrParts helper2(.data(bothInputs), .result(result2));
// Expect both result1 and result2 to be equal so...
assign result = result1 & result2;
endmodule
module OrParts(
input logic [15:0] data,
output logic [7:0] result
);
always_comb begin
result = data[15:8] | data[7:0];
end
endmodule
`default_nettype none
module Device(
input wire [7:0] a, b,
output wire [7:0] result
);
wire [7:0] result1, result2;
OrParts helper1(.data({a, b}), .result(result1));
wire [15:0] bothInputs;
assign bothInputs = {a, b};
OrParts helper2(.data(bothInputs), .result(result2));
// Expect both result1 and result2 to be equal so...
assign result = result1 & result2;
endmodule
module OrParts(
input wire [15:0] data,
output reg [7:0] result
);
// Update the module input definition since it is assigned in an always block
always @* begin
result = data[15:8] | data[7:0];
end
endmodule
`default_nettype none
module top;
reg [7:0] a, b;
wire [7:0] result;
Device dut(
.a(a),
.b(b),
.result(result)
);
initial begin
$monitor($time, " %b | %b = %b", a, b, result);
{a, b} = 16'h0;
#10 {a, b} = 16'h0102;
#10 {a, b} = 16'hff00;
#10 {a, b} = 16'h00ff;
#10 {a, b} = 16'hf0f0;
#10 {a, b} = 16'h0ff0;
#10 $finish;
end
endmodule
`default_nettype none
module Device(
input logic [31:0] data,
output logic parity
);
logic [3:0] partParity;
// This is passing a bit-slice as the input to the module assuming that all
// of the ports are connected in order.
Helper bottom(data[7:0], partParity[0]);
// This is the most common syntax explicitly binding the names of the
// connections to avoid errors where the order of the port definitions change.
Helper bottomMid(.parity(partParity[1]), .data(data[15:8]));
Wrapper1 topMid(.data(data[23:16]), .parity(partParity[2]));
Wrapper2 top(.data(data[31:24]), .parity1(partParity[3]));
assign parity = ^partParity;
endmodule
module Helper(
input logic [7:0] data,
output logic parity
);
// This is a bit-wise reduction operator
assign parity = ^data;
endmodule
module Wrapper1(
input logic [7:0] data,
output logic parity
);
// This is SystemVerilog shorthand to make it easy to write trivial systems.
// For the most part the wire names don't line up nicely so this doesn't
// work. The compiler replaces ".*" with ".data(data), .parity(parity)" and
// typically checks that the port widths are consistent.
Helper doTheRightThingMode(.*);
endmodule
module Wrapper2(
input logic [7:0] data,
output logic parity1
);
// This is a SystemVerilog shorthand similar to .* but actually usable in
// real projects since it only applies to a specific port. The compiler
// replaces the ".data" as ".data(data)" and typically checks that the port
// widths are consistent.
Helper compilerSaveMe(.data, .parity(parity1));
endmodule
\ No newline at end of file
`default_nettype none
module Device(
input wire [31:0] data,
output wire parity
);
wire [3:0] partParity;
Helper bottom(data[7:0], partParity[0]);
Helper bottomMid(.parity(partParity[1]), .data(data[15:8]));
Wrapper1 topMid(.data(data[23:16]), .parity(partParity[2]));
Wrapper2 top(.data(data[31:24]), .parity1(partParity[3]));
assign parity = ^partParity;
endmodule
module Helper(
input wire [7:0] data,
output wire parity
);
// This is a bit-wise reduction operator
assign parity = ^data;
endmodule
module Wrapper1(
input wire [7:0] data,
output wire parity
);
// Expand .* from SystemVerilog
Helper doTheRightThingMode(.data(data), .parity(parity));
endmodule
module Wrapper2(
input wire [7:0] data,
output wire parity1
);
// Expand .data from SystemVerilog
Helper compilerSaveMe(.data(data), .parity(parity1));
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg [31:0] data;
wire parity;
Device dut(
.data(data),
.parity(parity)
);
initial begin
$monitor($time, " data: %h parity: %b", data, parity);
data = 32'b0;
#10 data = 32'h00000003;
#10 data = 32'h00000300;
#10 data = 32'h00030000;
#10 data = 32'h03000000;
#10 data = 32'h01010101;
#10 data = 32'h01000101;
#10 data = 32'h00010101;
#10 data = 32'h01010001;
#10 data = 32'h01010100;
#10 data = 32'hffffffff;
#10 data = 32'hfeffffff;
#10 $finish;
end
endmodule
#!/bin/sh
assertExists() {
file=$1
[ -f "$file" ]
assertTrue "$file does not exist" $?
}
# USAGE: simulate <vcd-outfile> <top-module> <file> [<file> ...]
simulate() {
# arguments
sim_outfile="$1"; shift
sim_top="$1"; shift
# compile the files
sim_prog="$SHUNIT_TMPDIR/simprog.exe"
iverilog \
-o "$sim_prog" \
-g2005 \
-DTEST_VCD="\"$sim_outfile\"" \
-DTEST_TOP=$sim_top \
"tb_dumper.v" \
"$@"
assertTrue "iverilog on $1 failed" $?
# run the simulation
$sim_prog > /dev/null
assertTrue "simulating $1 failed" $?
# remove the date from the VCD
sed -i.orig -e "1,3d" "$sim_outfile"
}
runTest() {
test=$1
assertNotNull "test not specified" $test
sv="$test.sv"
ve="$test.v"
tb="${test}_tb.v"
assertExists $sv
assertExists $ve
assertExists $sv
# convert the SystemVerilog source file
cv="$SHUNIT_TMPDIR/conv-$test.v"
../../bin/sv2v $sv 2> /dev/null > $cv
assertTrue "conversion failed" $?
assertExists $cv
ref_vcd="$SHUNIT_TMPDIR/ref.vcd"
gen_vcd="$SHUNIT_TMPDIR/gen.vcd"
# simulate and compare the two files
simulate "$ref_vcd" top "$ve" "$tb"
simulate "$gen_vcd" top "$cv" "$tb"
diff "$ref_vcd" "$gen_vcd" > /dev/null
assertTrue "VCDs are different" $?
}
suite() {
for test in `ls *.sv | sed -e "s_\.sv\\\$__"`; do
eval "test_$test() { runTest \"$test\"; }"
suite_addTest "test_$test"
done
}
. shunit2
`default_nettype none
interface SimpleInterface(input logic clock, clear);
logic [31:0] data;
logic shift;
modport Producer(
output data,
input shift,
input clock, clear
);
modport Consumer(
input data,
output shift,
input clock, clear
);
endinterface : SimpleInterface
module Device(
input logic [7:0] dataIn,
output logic [31:0] dataOut,
input logic clock, clear
);
SimpleInterface theInterface(.clock, .clear);
Producer producer(
.myInterface(theInterface.Producer),
.dataIn
);
Consumer consumer(
.myInterface(theInterface.Consumer),
.dataOut
);
endmodule
// The producer takes the input and then transforms it for 4 cycles
module Producer(
SimpleInterface.Producer myInterface,
input logic [7:0] dataIn
);
logic [31:0] inProgress;
always_ff @(posedge myInterface.clock) begin
if(myInterface.clear) begin
inProgress <= 32'b0;
end else if(myInterface.shift) begin
inProgress <= {inProgress[23:0], dataIn};
end
end
assign myInterface.data = inProgress;
endmodule
module Consumer(
SimpleInterface.Consumer myInterface,
output logic [31:0] dataOut
);
// Just want this variable to make the test bench nicer
logic local_shift;
assign local_shift = myInterface.shift;
always_ff @(posedge myInterface.clock)
if(myInterface.clear) begin
myInterface.shift <= 1'b0;
end else begin
myInterface.shift <= ~myInterface.shift;
end
assign dataOut = myInterface.data;
endmodule
\ No newline at end of file
// Ignored the compiler directive
module Device(
input wire [7:0] dataIn,
output wire [31:0] dataOut,
input wire clock, clear
);
// Expanded interface declaration
wire theInterface_clock;
wire theInterface_clear;
wire [31:0] theInterface_data;
wire theInterface_shift;
// Interface instantiation
assign theInterface_clock = clock;
assign theInterface_clear = clear;
Producer producer(
// Expanded interface
.myInterface_clock(theInterface_clock),
.myInterface_clear(theInterface_clear),
.myInterface_data(theInterface_data),
.myInterface_shift(theInterface_shift),
.dataIn(dataIn)
);
Consumer consumer(
// Expanded interface
.myInterface_clock(theInterface_clock),
.myInterface_clear(theInterface_clear),
.myInterface_data(theInterface_data),
.myInterface_shift(theInterface_shift),
.dataOut(dataOut)
);
endmodule
module Producer(
// Port direction from SimpleInterface.Producer modport
input wire myInterface_clock,
input wire myInterface_clear,
output wire [31:0] myInterface_data,
input wire myInterface_shift,
input wire [7:0] dataIn
);
reg [31:0] inProgress;
always @(posedge myInterface_clock) begin
if(myInterface_clear) begin
inProgress <= 32'b0;
end else if(myInterface_shift) begin
inProgress <= {inProgress[23:0], dataIn};
end
end
assign myInterface_data = inProgress;
endmodule
module Consumer(
// Port direction from SimpleInterface.Consumer modport
input wire myInterface_clock,
input wire myInterface_clear,
input wire [31:0] myInterface_data,
output reg myInterface_shift,
output wire [31:0] dataOut
);
// Just want this variable to make the test bench nicer
wire local_shift;
assign local_shift = myInterface_shift;
always @(posedge myInterface_clock)
if(myInterface_clear) begin
myInterface_shift <= 1'b0;
end else begin
myInterface_shift <= ~myInterface_shift;
end
assign dataOut = myInterface_data;
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg [7:0] dataIn;
wire [31:0] dataOut;
reg clock, clear;
Device dut(
.dataIn(dataIn),
.dataOut(dataOut),
.clock(clock),
.clear(clear)
);
// Just some random test bench code to make sure it works as expected
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time," dataIn: %h dataOut: %h shift: %b", dataIn, dataOut, dut.consumer.local_shift);
clear <= 1'b1;
dataIn <= 8'h0;
repeat(5) @(posedge clock);
clear <= 1'b0;
@(posedge clock);
dataIn <= 8'h44;
@(posedge clock);
dataIn <= 8'h77;
@(posedge clock);
dataIn <= 8'h11;
@(posedge clock);
dataIn <= 8'h0;
repeat(5) @(posedge clock);
$finish;
end
endmodule
`default_nettype none
module Device(
input logic [7:0] doubleNibble,
output logic [3:0] sum
);
// I would probably write the example instance2, but that is mostly me just
// being overly cautious.
Helper instance1(doubleNibble[7:4], doubleNibble[3:0], sum);
logic [3:0] ignored;
Helper instance2(
.a(doubleNibble[7:4]),
.b(doubleNibble[3:0]),
.result(ignored)
);
endmodule
module Helper(
input logic [3:0] a, b,
output logic [3:0] result
);
assign result = a + b;
endmodule
\ No newline at end of file
`default_nettype none
module Device(
input wire [7:0] doubleNibble,
output wire [3:0] sum
);
// I would probably write the example instance2, but that is mostly me just
// being overly cautious.
Helper instance1(doubleNibble[7:4], doubleNibble[3:0], sum);
wire [3:0] ignored;
Helper instance2(
.a(doubleNibble[7:4]),
.b(doubleNibble[3:0]),
.result(ignored)
);
endmodule
module Helper(
input wire [3:0] a, b,
output wire [3:0] result
);
assign result = a + b;
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg [7:0] doubleNibble;
wire [3:0] sum;
Device dut(
.doubleNibble(doubleNibble),
.sum(sum)
);
reg [8:0] i; // Note that i is 1 bit wider than doubleNibble
initial begin
$monitor($time, " %h + %h = %h", doubleNibble[7:4], doubleNibble[3:0], sum);
doubleNibble = 8'h00;
for(i = 0; i <= 8'hff; i = i + 8'h1) begin
#10 doubleNibble = i; // This drops upper order bits
end
end
endmodule
`default_nettype none
typedef logic [31:0] Word_t;
// "packed" structs are effectively a single bit-vector that has syntactic sugar
// "for access specific parts
typedef struct packed {
Word_t data;
logic valid;
} InnerStruct_t;
typedef enum {MODE_A, MODE_B} Mode_t;
typedef struct packed {
InnerStruct_t inner;
Mode_t mode;
} OuterStruct_t;
module Example(
input logic clock, clear,
output logic success
);
Word_t data_in, data_out;
logic valid_in, valid_out;
Mode_t mode_in, mode_out;
OuterStruct_t object;
always_ff @(posedge clock) begin
if(clear) begin
data_in <= 32'h0;
valid_in <= 1'b0;
mode_in <= MODE_A;
end else if(mode_in == MODE_A) begin
valid_in <= 1'b1;
mode_in <= MODE_B;
// $bits is a built-in SystemVerilog function that determines how
// wide a data-type is (like sizeof in C)
data_in <= data_in + $bits(object);
end else begin
mode_in <= MODE_A;
data_in <= data_in + 32'h1;
valid_in <= 1'b1;
end
end
StructCompose compose(
.data(data_in),
.valid(valid_in),
.mode(mode_in),
.object
);
StructDecompose decompose(
.data(data_out),
.valid(valid_out),
.mode(mode_out),
.object
);
always_comb begin
success = 1'b0;
if(data_in == data_out)
if(valid_in == valid_out)
if(mode_in == mode_out)
success = 1'b1;
end
endmodule
module StructCompose(
input Word_t data,
input logic valid,
input Mode_t mode,
output OuterStruct_t object
);
always_comb begin
object = '{
inner: '{
data: data,
valid: valid
},
mode: mode
};
end
endmodule
module StructDecompose(
input OuterStruct_t object,
output Word_t data,
output logic valid,
output Mode_t mode
);
always_comb begin
data = object.inner.data;
valid = object.inner.valid;
mode = object.mode;
end
endmodule
\ No newline at end of file
`default_nettype none
// Technically the tool could do something much smarter than having a bunch of
// macros everywhere, but this is the easiest way I know to make it clear what
// is happening.
// "packed" structs are effectively a single bit-vector that has syntactic sugar
// to access specific parts. SystemVerilog struct layout is amusingly the
// complete opposite of C struct layout
`define InnerStruct_t_data [32:1]
`define InnerStruct_t_valid [0:0]
`define MODE_T\
parameter MODE_A = 32'b0;\
parameter MODE_B = 32'b1
// Nested structs are a bit trickier to resolve
`define OuterStruct_t_inner [64:32]
`define OuterStruct_t_mode [31:0]
module Example(
input wire clock, clear,
output reg success
);
`MODE_T;
// Verilog must unfuse these declarations since *_in is a reg and *_out is a wire
reg [31:0] data_in;
wire [31:0] data_out;
reg valid_in;
wire valid_out;
reg [31:0] mode_in;
wire [31:0] mode_out;
wire [64:0] object;
// Sadly Verilog doesn't support built-in functions like $bits or $clog2
// (these are the only functions that I've used in my core and would expect
// to synthesize. Luckily the implementation is easy-ish)
always @(posedge clock) begin
if(clear) begin
data_in <= 32'h0;
valid_in <= 1'b0;
mode_in <= MODE_A;
end else if(mode_in == MODE_A) begin
valid_in <= 1'b1;
mode_in <= MODE_B;
data_in <= data_in + 32'd65; // Magically computed by looking at the type object
end else begin
mode_in <= MODE_A;
data_in <= data_in + 32'h1;
valid_in <= 1'b1;
end
end
StructCompose compose(
.data(data_in),
.valid(valid_in),
.mode(mode_in),
.object(object)
);
StructDecompose decompose(
.data(data_out),
.valid(valid_out),
.mode(mode_out),
.object(object)
);
always @* begin
success = 1'b0;
if(data_in == data_out)
if(valid_in == valid_out)
if(mode_in == mode_out)
success = 1'b1;
end
endmodule
module StructCompose(
input wire [31:0] data,
input wire valid,
input wire [31:0] mode,
output reg [64:0] object
);
// Technically the tool could inline all of this, but it's easier to
// understand if the structs are built separately.
reg [32:0] __inner;
always @* begin
__inner `InnerStruct_t_data = data;
__inner `InnerStruct_t_valid = valid;
object `OuterStruct_t_mode = mode;
object `OuterStruct_t_inner = __inner;
end
endmodule
module StructDecompose(
input wire [64:0] object,
output reg [31:0] data,
output reg valid,
output reg [31:0] mode
);
// Technically the tool could inline the bit selection directly
reg [32:0] __inner;
always @* begin
__inner = object `OuterStruct_t_inner;
data = __inner `InnerStruct_t_data;
valid = __inner `InnerStruct_t_valid;
mode = object `OuterStruct_t_mode;
end
endmodule
\ No newline at end of file
`default_nettype none
module top;
reg clock, clear;
wire success;
Example dut(
.clock(clock),
.clear(clear),
.success(success)
);
initial begin
clock = 1;
forever #5 clock = ~clock;
end
initial begin
$monitor($time, " success: %b data_out: %h mode_out: %h", success, dut.data_out, dut.mode_out);
clear = 1'b1;
repeat(3) @(posedge clock);
clear = 1'b0;
repeat(20) @(posedge clock);
$finish;
end
endmodule
module sv2v_dumper;
initial begin
$dumpfile(`TEST_VCD);
$dumpvars(1, `TEST_TOP);
end
endmodule
`default_nettype none
typedef logic [31:0] Word_t;
module Example(
input logic [3:0] data_in,
output logic [31:0] data_out
);
Word_t word;
always_comb begin
// This is the repeat operator
word = {8{data_in}};
end
assign data_out = word;
endmodule
`default_nettype none
// Verilog unwraps the typedef
module Example(
input wire [3:0] data_in,
output wire [31:0] data_out
);
reg [31:0] word;
always @* begin
// This is the repeat operator
word = {8{data_in}};
end
assign data_out = word;
endmodule
`default_nettype none
module top;
reg [3:0] data_in;
wire [31:0] data_out;
Example dut(
.data_in(data_in),
.data_out(data_out)
);
reg [4:0] i; // More bits than data_in
initial begin
$monitor($time, " data_in: %h data_out: %h", data_in, data_out);
data_in = 4'h0;
for(i = 5'b0; i <= 4'hf; i = i + 4'd1)
#10 data_in = i;
#10 $finish;
end
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment