diff --git a/Makefile b/Makefile index 429e9da..2a3a6ab 100644 --- a/Makefile +++ b/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) diff --git a/test/relong/README.md b/test/relong/README.md new file mode 100644 index 0000000..daa267a --- /dev/null +++ b/test/relong/README.md @@ -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. diff --git a/test/relong/alu.sv b/test/relong/alu.sv new file mode 100644 index 0000000..f331237 --- /dev/null +++ b/test/relong/alu.sv @@ -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 \ No newline at end of file diff --git a/test/relong/alu.v b/test/relong/alu.v new file mode 100644 index 0000000..327809b --- /dev/null +++ b/test/relong/alu.v @@ -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 \ No newline at end of file diff --git a/test/relong/alu_tb.v b/test/relong/alu_tb.v new file mode 100644 index 0000000..fe8fe1e --- /dev/null +++ b/test/relong/alu_tb.v @@ -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 diff --git a/test/relong/array.sv b/test/relong/array.sv new file mode 100644 index 0000000..7237c8d --- /dev/null +++ b/test/relong/array.sv @@ -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 \ No newline at end of file diff --git a/test/relong/array.v b/test/relong/array.v new file mode 100644 index 0000000..bb984fa --- /dev/null +++ b/test/relong/array.v @@ -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 \ No newline at end of file diff --git a/test/relong/array_tb.v b/test/relong/array_tb.v new file mode 100644 index 0000000..893133e --- /dev/null +++ b/test/relong/array_tb.v @@ -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 diff --git a/test/relong/case.sv b/test/relong/case.sv new file mode 100644 index 0000000..809e3f7 --- /dev/null +++ b/test/relong/case.sv @@ -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/ \ No newline at end of file diff --git a/test/relong/case.v b/test/relong/case.v new file mode 100644 index 0000000..b369952 --- /dev/null +++ b/test/relong/case.v @@ -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 diff --git a/test/relong/case_tb.v b/test/relong/case_tb.v new file mode 100644 index 0000000..fb0e9b0 --- /dev/null +++ b/test/relong/case_tb.v @@ -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 diff --git a/test/relong/double_clock.sv b/test/relong/double_clock.sv new file mode 100644 index 0000000..407729e --- /dev/null +++ b/test/relong/double_clock.sv @@ -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 \ No newline at end of file diff --git a/test/relong/double_clock.v b/test/relong/double_clock.v new file mode 100644 index 0000000..b8986b7 --- /dev/null +++ b/test/relong/double_clock.v @@ -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 \ No newline at end of file diff --git a/test/relong/double_clock_tb.v b/test/relong/double_clock_tb.v new file mode 100644 index 0000000..b55c955 --- /dev/null +++ b/test/relong/double_clock_tb.v @@ -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 diff --git a/test/relong/enum.sv b/test/relong/enum.sv new file mode 100644 index 0000000..6ab36c2 --- /dev/null +++ b/test/relong/enum.sv @@ -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 \ No newline at end of file diff --git a/test/relong/enum.v b/test/relong/enum.v new file mode 100644 index 0000000..d9f1527 --- /dev/null +++ b/test/relong/enum.v @@ -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 \ No newline at end of file diff --git a/test/relong/enum_tb.v b/test/relong/enum_tb.v new file mode 100644 index 0000000..ba66995 --- /dev/null +++ b/test/relong/enum_tb.v @@ -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 diff --git a/test/relong/fsm.sv b/test/relong/fsm.sv new file mode 100644 index 0000000..c9ee9ee --- /dev/null +++ b/test/relong/fsm.sv @@ -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 \ No newline at end of file diff --git a/test/relong/fsm.v b/test/relong/fsm.v new file mode 100644 index 0000000..620851c --- /dev/null +++ b/test/relong/fsm.v @@ -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 \ No newline at end of file diff --git a/test/relong/fsm_tb.v b/test/relong/fsm_tb.v new file mode 100644 index 0000000..1c343b1 --- /dev/null +++ b/test/relong/fsm_tb.v @@ -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 diff --git a/test/relong/functions.sv b/test/relong/functions.sv new file mode 100644 index 0000000..a1c565c --- /dev/null +++ b/test/relong/functions.sv @@ -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 \ No newline at end of file diff --git a/test/relong/functions.v b/test/relong/functions.v new file mode 100644 index 0000000..a839346 --- /dev/null +++ b/test/relong/functions.v @@ -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 \ No newline at end of file diff --git a/test/relong/functions_tb.v b/test/relong/functions_tb.v new file mode 100644 index 0000000..ddbe3d3 --- /dev/null +++ b/test/relong/functions_tb.v @@ -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 diff --git a/test/relong/inline_concat.sv b/test/relong/inline_concat.sv new file mode 100644 index 0000000..36d68f1 --- /dev/null +++ b/test/relong/inline_concat.sv @@ -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 diff --git a/test/relong/inline_concat.v b/test/relong/inline_concat.v new file mode 100644 index 0000000..30c1478 --- /dev/null +++ b/test/relong/inline_concat.v @@ -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 diff --git a/test/relong/inline_concat_tb.v b/test/relong/inline_concat_tb.v new file mode 100644 index 0000000..2e49491 --- /dev/null +++ b/test/relong/inline_concat_tb.v @@ -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 diff --git a/test/relong/port_connections.sv b/test/relong/port_connections.sv new file mode 100644 index 0000000..a333aac --- /dev/null +++ b/test/relong/port_connections.sv @@ -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 \ No newline at end of file diff --git a/test/relong/port_connections.v b/test/relong/port_connections.v new file mode 100644 index 0000000..7c58750 --- /dev/null +++ b/test/relong/port_connections.v @@ -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 \ No newline at end of file diff --git a/test/relong/port_connections_tb.v b/test/relong/port_connections_tb.v new file mode 100644 index 0000000..6c40235 --- /dev/null +++ b/test/relong/port_connections_tb.v @@ -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 diff --git a/test/relong/run.sh b/test/relong/run.sh new file mode 100755 index 0000000..0297457 --- /dev/null +++ b/test/relong/run.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +assertExists() { + file=$1 + [ -f "$file" ] + assertTrue "$file does not exist" $? +} + +# USAGE: simulate [ ...] +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 diff --git a/test/relong/simple_interface.sv b/test/relong/simple_interface.sv new file mode 100644 index 0000000..2173fb7 --- /dev/null +++ b/test/relong/simple_interface.sv @@ -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 \ No newline at end of file diff --git a/test/relong/simple_interface.v b/test/relong/simple_interface.v new file mode 100644 index 0000000..645779a --- /dev/null +++ b/test/relong/simple_interface.v @@ -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 \ No newline at end of file diff --git a/test/relong/simple_interface_tb.v b/test/relong/simple_interface_tb.v new file mode 100644 index 0000000..fec5164 --- /dev/null +++ b/test/relong/simple_interface_tb.v @@ -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 diff --git a/test/relong/split_ports.sv b/test/relong/split_ports.sv new file mode 100644 index 0000000..80df057 --- /dev/null +++ b/test/relong/split_ports.sv @@ -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 \ No newline at end of file diff --git a/test/relong/split_ports.v b/test/relong/split_ports.v new file mode 100644 index 0000000..f1d731e --- /dev/null +++ b/test/relong/split_ports.v @@ -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 \ No newline at end of file diff --git a/test/relong/split_ports_tb.v b/test/relong/split_ports_tb.v new file mode 100644 index 0000000..dcf468e --- /dev/null +++ b/test/relong/split_ports_tb.v @@ -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 diff --git a/test/relong/struct.sv b/test/relong/struct.sv new file mode 100644 index 0000000..d6bd99e --- /dev/null +++ b/test/relong/struct.sv @@ -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 \ No newline at end of file diff --git a/test/relong/struct.v b/test/relong/struct.v new file mode 100644 index 0000000..aef8a56 --- /dev/null +++ b/test/relong/struct.v @@ -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 \ No newline at end of file diff --git a/test/relong/struct_tb.v b/test/relong/struct_tb.v new file mode 100644 index 0000000..360823f --- /dev/null +++ b/test/relong/struct_tb.v @@ -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 diff --git a/test/relong/tb_dumper.v b/test/relong/tb_dumper.v new file mode 100644 index 0000000..7bd7ec0 --- /dev/null +++ b/test/relong/tb_dumper.v @@ -0,0 +1,6 @@ +module sv2v_dumper; + initial begin + $dumpfile(`TEST_VCD); + $dumpvars(1, `TEST_TOP); + end +endmodule diff --git a/test/relong/typedef.sv b/test/relong/typedef.sv new file mode 100644 index 0000000..f0c2ce7 --- /dev/null +++ b/test/relong/typedef.sv @@ -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 + + + diff --git a/test/relong/typedef.v b/test/relong/typedef.v new file mode 100644 index 0000000..99d7d77 --- /dev/null +++ b/test/relong/typedef.v @@ -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 + + + diff --git a/test/relong/typedef_tb.v b/test/relong/typedef_tb.v new file mode 100644 index 0000000..6d4220e --- /dev/null +++ b/test/relong/typedef_tb.v @@ -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