mirror of https://github.com/zachjs/sv2v.git
added hdl-examples tests
This commit is contained in:
parent
6cc4654ad6
commit
4b3966aa25
5
Makefile
5
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# 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.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
`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/
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#!/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
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// 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
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
`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
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
module sv2v_dumper;
|
||||
initial begin
|
||||
$dumpfile(`TEST_VCD);
|
||||
$dumpvars(1, `TEST_TOP);
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
`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
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
`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
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
`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
|
||||
Loading…
Reference in New Issue