Implementing Synopsys Simulation Workflow with VCS and Verdi
Arithmetic Components
The design utilizes separate modules for addition, subtraction, multiplication, and division operations.
module math_add (
input wire [7:0] x,
input wire [7:0] y,
output wire [7:0] z
);
assign z = x + y;
endmodule
module math_sub (
input wire [7:0] x,
input wire [7:0] y,
output wire [7:0] z
);
assign z = x - y;
endmodule
module math_mul (
input wire [7:0] x,
input wire [7:0] y,
output wire [7:0] z
);
assign z = x * y;
endmodule
module math_div (
input wire [7:0] x,
input wire [7:0] y,
output wire [7:0] quotient,
output wire [7:0] rem
);
assign quotient = x / y;
assign rem = x % y;
endmodule
Top-Level Integration
The core processor module instantiates the arithmetic units and multiplexes the output based on the opcode.
module core_processor (
input wire clk,
input wire rst_n,
input wire [2:0] opcode,
input wire [7:0] src_a,
input wire [7:0] src_b,
output reg [7:0] data_out
);
localparam CMD_ADD = 3'd0;
localparam CMD_SUB = 3'd1;
localparam CMD_MUL = 3'd2;
localparam CMD_DIV = 3'd3;
reg [7:0] w_add_a, w_add_b, w_add_res;
reg [7:0] w_sub_a, w_sub_b, w_sub_res;
reg [7:0] w_mul_a, w_mul_b, w_mul_res;
reg [7:0] w_div_a, w_div_b, w_div_res;
math_add u_add (.x(w_add_a), .y(w_add_b), .z(w_add_res));
math_sub u_sub (.x(w_sub_a), .y(w_sub_b), .z(w_sub_res));
math_mul u_mul (.x(w_mul_a), .y(w_mul_b), .z(w_mul_res));
math_div u_div (.x(w_div_a), .y(w_div_b), .quotient(w_div_res));
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_out <= 8'd0;
end else begin
case (opcode)
CMD_ADD: begin
w_add_a <= src_a;
w_add_b <= src_b;
data_out <= w_add_res;
end
CMD_SUB: begin
w_sub_a <= src_a;
w_sub_b <= src_b;
data_out <= w_sub_res;
end
CMD_MUL: begin
w_mul_a <= src_a;
w_mul_b <= src_b;
data_out <= w_mul_res;
end
CMD_DIV: begin
w_div_a <= src_a;
w_div_b <= src_b;
data_out <= w_div_res;
end
default: data_out <= 8'd0;
endcase
end
end
endmodule
Testbench
The verification environment drives the design with a clock and reset sequence, applying random operands to verify logic.
`timescale 1ns/1ps
module tb_processor;
reg clk;
reg rst_n;
reg [2:0] opcode;
reg [7:0] src_a;
reg [7:0] src_b;
wire [7:0] data_out;
localparam CMD_ADD = 3'd0;
localparam CMD_SUB = 3'd1;
localparam CMD_MUL = 3'd2;
localparam CMD_DIV = 3'd3;
core_processor u_dut (
.clk(clk),
.rst_n(rst_n),
.opcode(opcode),
.src_a(src_a),
.src_b(src_b),
.data_out(data_out)
);
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rst_n = 0;
opcode = CMD_ADD;
src_a = 0;
src_b = 0;
#20;
rst_n = 1;
#10;
@(posedge clk);
src_a = 5;
src_b = 3;
opcode = CMD_ADD;
repeat(2) @(posedge clk);
$display("Add: %d + %d = %d", src_a, src_b, data_out);
#20;
@(posedge clk);
src_a = 10;
src_b = 4;
opcode = CMD_SUB;
repeat(2) @(posedge clk);
$display("Sub: %d - %d = %d", src_a, src_b, data_out);
#20;
@(posedge clk);
src_a = 20;
src_b = 5;
opcode = CMD_DIV;
repeat(2) @(posedge clk);
$display("Div: %d / %d = %d", src_a, src_b, data_out);
#20;
@(posedge clk);
src_a = 6;
src_b = 7;
opcode = CMD_MUL;
repeat(2) @(posedge clk);
$display("Mul: %d * %d = %d", src_a, src_b, data_out);
#20;
$finish;
end
endmodule
Build Automation
The Makefile manages the compilation with VCS, simulation execution with FSDB dumping, and the invocation of Verdi for debugging.
# Project Configuration
TOP_MODULE = core_processor
FILE_LIST = files.f
WAVES = $(TOP_MODULE).fsdb
.PHONY: all compile simulate run_verdi clean
all: clean compile simulate
compile:
vcs -sverilog -debug_all -full64 \
-P $(NOVAS_HOME)/share/PLI/VCS/LINUX64/novas.tab \
$(NOVAS_HOME)/share/PLI/VCS/LINUX64/pli.a \
-f $(FILE_LIST) \
-l compile.log
simulate:
./simv \
-ucli -i dump_fsdb.tcl \
+fsdb+autoflush \
-l simulate.log
run_verdi:
verdi -nologo -sv -f $(FILE_LIST) -ssf $(WAVES) &
clean:
rm -rf csrc DVEfiles simv simv.daidir ucli.key *.log *.fsdb *.conf *.dat
FSDB Dump Script
global env
set fsdb_file "core_processor.fsdb"
fsdbDumpfile "$fsdb_file"
fsdbDumpvars 0 "tb_processor"
run