FPGA verilog代码:原码除法器设计
一、模块功能
用于实现FPGA中两个8位有符号数的除法,已经过验证。
二、模块接口:
1、clk: 输入时钟,用于同步模块的操作。
2、rst: 复位信号,高电平有效。
3、x: 8位宽的输入线,表示被除数。
4、y: 8位宽的输入线,表示除数。
5、start: 启动信号,当它被激活时,模块开始执行除法操作。
6、z: 8位宽的输出线,表示除法操作的结果。
7、r: 8位宽的寄存器输出,根据注释,它似乎用于存储除法操作的中间结果或最终结果的一部分。
8、busy: 1位宽的寄存器输出,表示模块是否正在忙碌处理除法操作。当busy为高时,表示模块正在执行除法操作。
三、模块代码
`timescale 1ns / 1ps
module divider (
input wire clk,
input wire rst,
input wire [7:0] x,
input wire [7:0] y,
input wire start,
output wire [7:0] z,
output reg [7:0] r=0,
output reg busy=0
);
// TODO
parameter N=8;
localparam S=N<<1;
reg [N-1:0] tempa=0;
reg [N-1:0] tempb=0;
reg [S-1:0] temp_a=0;
reg [S-1:0] temp_b=0;
reg [5:0] st=0;
localparam st_idle = 6'b000000;
localparam st_init = 6'b000001;
localparam st_calc1 = 6'b000010;
localparam st_calc2 = 6'b000100;
localparam st_done = 6'b001000;
reg [N-1:0] i=0;
reg[N-1:0] z_reg=0;
wire sign ;
assign sign = (x[N-1] ^ y[N-1]);
assign z = z_reg ;
always @(posedge clk) begin
if(rst) begin
busy <= 1'b0;
st <= st_idle;
end else begin
case (st)
st_idle: begin
i <= 1'h0;
if(start) begin
busy <= 1'b1;
// tempa <= {x[N-1] == 1'd1}? {1'd0,~x[N-2:0]+1'd1} :{1'd0,x[N-2:0]};
// tempb <= {y[N-1] == 1'd1}? {1'd0,~y[N-2:0]+1'd1} :{1'd0,y[N-2:0]};
tempa <= x[N-2:0] ;
tempb <= y[N-2:0] ;
st <= st_init;
end else begin
st <= st_idle;
end
end
st_init: begin
temp_a <= {{N{1'b0}},tempa};
temp_b <= {tempb,{N{1'b0}}};
st <= st_calc1;
busy <= 1'b1;
end
st_calc1: begin
busy <= 1'b1;
if(i < N) begin
temp_a <= {temp_a[S-2:0],1'b0};
st <= st_calc2;
end else begin
st <= st_done;
end
end
st_calc2: begin
busy <= 1'b1;
if(temp_a[S-1:N] >= tempb) begin
temp_a <= temp_a - temp_b + 1'b1;
end else begin
temp_a <= temp_a;
end
i <= i + 1'b1;
st <= st_calc1;
end
st_done: begin
// z_reg <= (x[N-1]^y[N-1] == 1'd1)? {1'd1,~temp_a[N-2:0]+1'd1}:temp_a[N-1:0] ;
// r <= (x[N-1] == 1'B1) ? { 1'd1,~temp_a[S-2:N]+1'd1 } :temp_a[S-1:N];
z_reg <= (x[N-1]^y[N-1] == 1'd1)? {1'd1, temp_a[N-2:0] }:temp_a[N-1:0] ;
r <= (x[N-1] == 1'B1) ? { 1'd1, temp_a[S-2:N] } :temp_a[S-1:N];
busy <= 1'b0;
st <= st_idle;
end
default: begin
st <= st_idle;
end
endcase
end
end
endmodule
四、模块例化
divider DUT (
.clk (tb_clk),
.rst (tb_rst),
.x (dut_x),
.y (dut_y),
.start (dut_start),
.z (dut_z),
.r (dut_r),
.busy (dut_busy)
);
五、模块代码分析
1、内部信号和寄存器
(1)tempa, tempb: 8位宽的寄存器,用于存储除法操作中的临时值。
(2)temp_a, temp_b: 16位宽的寄存器,是tempa和tempb的扩展,用于存储更宽的数值。
(3)st: 6位宽的寄存器,用于存储当前状态机的状态。
(4)i: 8位宽的寄存器,用作循环计数器。
(5)z_reg: 8位宽的寄存器,用于存储最终的输出结果。
(6)sign: 1位宽的线信号,用于判断x和y的符号是否相同。
2、参数和局部参数:
(1)N: 参数,定义了操作数的位数,这里设置为8。
(2)S: 局部参数,是N的两倍,用于定义扩展操作数的位数。
六、总结
该除法器模块是一个实用的FPGA设计组件,它接收两个8位的输入x和y,以及一个启动信号start。当启动信号被激活时,模块开始执行除法操作,并将结果输出到z。模块还包含一个busy信号,表示模块是否正在忙碌处理除法操作。
该博客为原创文章,未经过博主同意不得转载。
本文的博客地址为:https://blog.csdn.net/qq_34895681/article/details/141279845?spm=1001.2014.3001.5501
七、附录
模块的仿真代码如下所示:
`timescale 1ns / 1ps
`define TEST_NUM 8
/* 8 Tests:
0. 14 / 3 = 4 ... 2
1. 111 / 31 = 3 ... 18
2. -73 / 6 = -12 ... -1
3. -49 / 11 = -4 ... -5
4. 120 / -33 = -3 ... 21
5. 23 / -15 = -1 ... 8
6. -19 / -10 = 1 ... -9
7. -53 / -25 = 2 ... -3
*/
module div_testbench();
reg tb_clk = 0;
reg tb_rst = 1;
reg dut_start;
wire [ 7:0] dut_x;
wire [ 7:0] dut_y;
wire dut_busy;
wire [ 7:0] dut_z;
wire [ 7:0] dut_r;
reg [ 7:0] tb_i;
reg [31:0] tb_data [`TEST_NUM-1:0];
wire [ 7:0] tb_dat1 = tb_data[tb_i][31:24];
wire [ 7:0] tb_dat2 = tb_data[tb_i][23:16];
wire [ 7:0] tb_ans_z = tb_data[tb_i][15: 8];
wire [ 7:0] tb_ans_r = tb_data[tb_i][ 7: 0];
assign dut_x = tb_dat1;
assign dut_y = tb_dat2;
localparam S0 = 3'b000;
localparam S1 = 3'b001;
localparam S2 = 3'b010;
localparam S3 = 3'b100;
reg [2:0] tb_state, tb_nstat;
wire tb_z_err = (tb_state == S3) && (dut_z != tb_ans_z) ? 1'b1 : 1'b0;
wire tb_r_err = (tb_state == S3) && (dut_r != tb_ans_r) ? 1'b1 : 1'b0;
initial begin
$readmemh("data.txt", tb_data);
#12 tb_rst = 0;
end
always #5 tb_clk = !tb_clk;
always @(posedge tb_clk or posedge tb_rst) begin
tb_state <= tb_rst ? S0 : tb_nstat;
end
always @(*) begin
case (tb_state)
S0: tb_nstat = tb_i < `TEST_NUM ? S1 : S0;
S1: tb_nstat = S2;
S2: tb_nstat = dut_busy ? S2 : S3;
S3: tb_nstat = S0;
default: tb_nstat = S0;
endcase
end
always @(posedge tb_clk or posedge tb_rst) begin
if (tb_rst) begin
dut_start <= 1'b0;
tb_i <= 8'h0;
end else begin
case (tb_state)
S0: begin
if (!dut_busy)
dut_start <= 1'b1;
end
S3: begin
tb_i <= tb_i + 8'h1;
if (!tb_z_err & !tb_r_err)
$display("Test %0d passed", tb_i);
if (tb_i == `TEST_NUM - 8'h1) begin
$display("All tests passed! Congratulations! Good job!");
$finish;
end
if (tb_z_err) begin
$display("Test %0d failed: z should be 0x%02x, but yours: 0x%02x", tb_i, tb_ans_z, dut_z);
$stop;
end
if (tb_r_err) begin
$display("Test %0d failed: r should be 0x%02x, but yours: 0x%02x", tb_i, tb_ans_r, dut_r);
$stop;
end
end
default: begin
dut_start <= 1'b0;
end
endcase
end
end
divider DUT (
.clk (tb_clk),
.rst (tb_rst),
.x (dut_x),
.y (dut_y),
.start (dut_start),
.z (dut_z),
.r (dut_r),
.busy (dut_busy)
);
endmodule