1.奇数分频电路,占空比50%
// 实现任意奇数分频,占空比为50%
module div_clk
# (parameter div_N = 3 , log_div_N = 2 , div_clk_half = 1) // div_N表示几分频
( // log_div_N表示比div_N大的最小2的整数次方的数的2的指数
input logic clk, // 例如比3大的最小的是4=2^2,所以log_div_N=2
input logic rstn, // 例如比5大的最小的是8=2^3,所以log_div_N=3
output logic div_clk // div_clk_half = (div_N - 1)/2 ,因为我们这都是奇数分频
);
logic [log_div_N-1:0] cnt;
logic pos_clk , neg_clk;
// cnt记录时钟周期,N分频就要每N个周期循环一次
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
cnt <= 'd0;
end
else begin
if(cnt == div_N - 1)
cnt <= 'd0;
else
cnt <= cnt + 1'b1;
end
end
// 对于N分频,我们让pos_clk成为周期为N,高电平持续 (N-1)/2 个周期的信号
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
pos_clk <= 1'b0;
end
else begin
if(cnt == 'd0 || cnt == div_clk_half) // 在第0个周期和第(N-1)/2个周期改变,高电平持续 (N-1)/2 个周期的信号
pos_clk <= ~pos_clk;
else
pos_clk <= pos_clk;
end
end
// 对于N分频,我们让neg_clk也成为周期为N,高电平持续(N-1)/2 个周期的信号
// 但是neg_clk为下降沿触发,则neg_clk和pos_clk错开半个周期,两者相或,则得到
// 占空比为50%的奇数N分频
always @(negedge clk or negedge rstn)begin
if(!rstn)begin
neg_clk <= 1'b0;
end
else begin
if(cnt == 'd1 || cnt == div_clk_half + 1'b1) // 因为错开了半个周期,所以在第0+1个周期和第((N-1)/2)+1个周期改变,高电平持续 (N-1)/2 个周期的信号
neg_clk <= ~neg_clk;
else
neg_clk <= neg_clk;
end
end
// 最后两个周期和占空比相同,但相位相差半个周期的信号相或,得到了占空比为50%的N分频时钟信号
assign div_clk = pos_clk | neg_clk;
endmodule
测试文件
`timescale 1ns/1ps
module tb ();
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#600 $finish; // 一定要设置一个仿真停止时间
end
logic clk,rstn,div_clk;
initial begin
clk = 0;
forever begin
#10 clk = ~clk;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
end
div_clk#(
.div_N ( 7 ),
.log_div_N ( 3 ),
.div_clk_half ( 3 )
)u_div_clk(
.clk ( clk ),
.rstn ( rstn ),
.div_clk ( div_clk )
);
endmodule
仿真波形
2.串并转换
module serial_parallel_conv
#(parameter WIDTH = 8 , LOG2_WIDTH = 3)
(
input logic clk,
input logic rstn,
input logic i_data,
output logic [WIDTH - 1 : 0] o_data
);
logic [LOG2_WIDTH: 0] cnt;
logic [WIDTH - 1 : 0] data_p;
always @(posedge clk or negedge rstn ) begin
if (!rstn) begin
cnt <= 'd0;
end
else begin
if(cnt == WIDTH-1)begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
// MSB优先:串行通信先发送最高有效位,存储在高位
always @(posedge clk or negedge rstn ) begin
if (!rstn) begin
data_p <= 'd0;
end
else begin
data_p <= {data_p[WIDTH - 2 : 0],i_data};
end
end
// // LSB优先:串行通信先发送最低有效位,存储在低位
// always @(posedge clk or negedge rstn ) begin
// if (!rstn) begin
// cnt <= 'd0;
// data_p <= 'd0;
// end
// else begin
// cnt <= cnt + 1'b1;
// data_p <= {i_data , data_p[WIDTH - 1 : 1]}
// end
// end
// 输出比输入结束时晚了一个时钟周期
// always @(posedge clk or negedge rstn) begin
// if(!rstn)begin
// o_data <= 'd0;
// end
// else begin
// o_data <= (cnt == 0) ? data_p : 'd0;
// end
// end
// 在输入结束时立即输出
assign o_data = (cnt == 0) ? data_p : 'd0;
endmodule
测试文件
`timescale 1ns/1ps
module tb();
// iverilog -g2005-sv -o sim.out tb.sv serial_parallel_conv.sv
// vvp -n sim.out
// gtkwave wave.vcd
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#1000 $finish; // 一定要设置一个仿真停止时间
end
logic clk,rstn,i_data;
logic [7:0] o_data;
initial begin
clk = 0;
forever begin
#10 clk = ~clk;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
#500 rstn = 0;
#15 rstn = 1;
end
initial begin
i_data = 1'd0;
#28 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
end
serial_parallel_conv#(
.WIDTH ( 8 ),
.LOG2_WIDTH ( 3 )
)u_serial_parallel_conv(
.clk ( clk ),
.rstn ( rstn ),
.i_data ( i_data ),
.o_data ( o_data )
);
endmodule
仿真波形
3.用verilog实现乘法y = a * b ,a和b都是8bit,考虑三种情况:
-
都是无符号数
-
都是有符号数
-
a是有符号数,b是无符号数
module t1(
input [ 7:0] i_a , //无符号数a
input [ 7:0] i_b , //无符号数b
output [15:0] o_y //乘法结果的位宽:a的位宽+b的位宽+0个符号位
);
assign o_y = i_a * i_b; // 位宽为8+8+0
endmodule
module t2(
input signed [ 7:0] i_a , //有符号数a
input signed [ 7:0] i_b , //有符号数b
output signed [14:0] o_y //乘法结果的位宽:a除去符号位的位宽+b除去符号位的位宽+1个符号位
);
assign o_y = i_a * i_b; //位宽为7+7+1
endmodule
3)module t3(
input signed [ 7:0] i_a , //有符号数a
input [ 7:0] i_b , //无符号数b
output signed [15:0] o_y //乘法结果的位宽:a除去符号位的位宽+b的位宽+1个符号位
);
wire signed [8:0] b_r;
assign b_r = {1'b0,i_b};//加0不会出错,因为正数的补码与原码相同
assign o_y = i_a * b_r;//7+8+1
endmodule
4.画出SRAM bit cell结构图
SRAM存储的原理就是基于双稳态电路,将两个反相器的输入与输出分别交叉连接,然后再利用两个MOS管作为字线进行选通,总共需要6个MOS管
5.序列检测1101
带重叠检测
module FSM_seq1101 (
input logic clk,
input logic rstn,
input logic i_data,
output logic o_out
);
parameter IDLE = 4'b0000 , S1 = 4'B0001 , S2 = 4'B0010 , S3 = 4'b0100, S4 = 4'b1000;
logic [3:0] state , next_state;
always@(*)begin
case(state)
IDLE: next_state <= i_data ? S1: IDLE;
S1: next_state <= i_data ? S2: IDLE;
S2: next_state <= i_data ? S2: S3;
S3: next_state <= i_data ? S4: IDLE;
S4: next_state <= i_data ? S2: IDLE;
default: next_state <= IDLE;
endcase
end
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
assign o_out = (state == S4)? 1'b1:1'b0;
endmodule
测试文件
`timescale 1ns/1ps
module tb();
// iverilog -g2005-sv -o sim.out tb.sv FSM_seq1101.sv
// vvp -n sim.out
// gtkwave wave.vcd
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#1000 $finish; // 一定要设置一个仿真停止时间
end
logic clk,rstn,i_data,o_out;
initial begin
clk = 0;
forever begin
#10 clk = ~clk;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
#500 rstn = 0;
#15 rstn = 1;
end
initial begin
i_data = 1'd0;
#28 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
end
FSM_seq1101 u_FSM_seq1101(
.clk ( clk ),
.rstn ( rstn ),
.i_data ( i_data ),
.o_out ( o_out )
);
endmodule
仿真波形
6.单bit跨时钟域处理
clk_a时钟域的信号同步到clk_b时钟域,分为几种情况:
(1)慢时钟到快时钟
(2)快时钟到慢时钟
(1)慢时钟到快时钟
// 单bit跨时钟域,慢时钟到快时钟
module onebit_sync_f2s (
input logic clk_a,
input logic clk_b,
input logic rstn,
input logic i_data,
output logic o_out
);
logic a_sig;
logic [1:0] b_sig;
// 在clk_a时钟域下打一拍
always @(posedge clk_a or negedge rstn) begin
if(!rstn)begin
a_sig <= 1'b0;
end
else begin
a_sig <= i_data;
end
end
// 在clk_b时钟域下打两拍,防止亚稳态
always@(posedge clk_b or negedge rstn) begin
if(!rstn)begin
b_sig <= 2'b0;
end
else begin
b_sig <= {b_sig[0],a_sig};
end
end
assign o_out = b_sig[1];
endmodule
测试文件
`timescale 1ns/1ps
module tb();
// iverilog -g2005-sv -o sim.out tb.sv onebit_sync_f2s.sv
// vvp -n sim.out
// gtkwave wave.vcd
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#1000 $finish; // 一定要设置一个仿真停止时间
end
logic clk_a,clk_b,rstn,i_data,o_out;
initial begin
clk_a = 0;
forever begin
#10 clk_a = ~clk_a;
end
end
initial begin
clk_b = 0;
forever begin
#4 clk_b = ~clk_b;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
#500 rstn = 0;
#15 rstn = 1;
end
initial begin
i_data = 1'd0;
#28 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
#20 i_data = 1'b1;
#20 i_data = 1'b0;
#20 i_data = 1'b1;
end
onebit_sync_f2s u_onebit_sync_f2s(
.clk_a ( clk_a ),
.clk_b ( clk_b ),
.rstn ( rstn ),
.i_data ( i_data ),
.o_out ( o_out )
);
endmodule
(2)快时钟到慢时钟
要将一个信号从快时钟域传递到慢时钟域,首先要在快时钟域展宽脉冲,然后将展宽的脉冲传递到慢时钟域,如果在慢时钟域检测到脉冲后就可以给快时钟域一个反馈以阻止脉冲的继续展宽;
module onebit_sync_s2f(
input logic clk_a,
input logic clk_b,
input logic rstn,
input logic i_data,
output logic o_out
);
logic a_sig,b_sig;
logic [1:0] b_sync,a_sync;
// 在clk_a下生成展宽信号a_sig
always @(posedge clk_a or negedge rstn) begin
if(!rstn)begin
a_sig <= 1'b0;
end
else begin
if(i_data == 1'b1)begin
a_sig = 1'b1;
end
else if(a_sync[1] == 1'b1)begin
a_sig = 1'b0;
end
else begin
a_sig = a_sig;
end
end
end
// 在clk_b下同步a_sig信号
always @(posedge clk_b or negedge rstn) begin
if(!rstn)begin
b_sig <= 1'b0;
end
else begin
b_sig <= a_sig;
end
end
// 在clk_b下打两拍,生成跨时钟域信号
always @(posedge clk_b or negedge rstn) begin
if(!rstn)begin
b_sync <= 2'b00;
end
else begin
b_sync <= {b_sync[0],b_sig};
end
end
assign o_out = b_sync[1];
// clk_b同步了信号后,生成一个信号使a_sig停止展宽
always @(posedge clk_a or negedge rstn) begin
if(!rstn)begin
a_sync <= 2'b00;
end
else begin
a_sync <= {a_sync[0],b_sync[1]};
end
end
endmodule
测试文件
`timescale 1ns/1ps
module tb();
// iverilog -g2005-sv -o sim.out tb.sv onebit_sync_s2f.sv
// vvp -n sim.out
// gtkwave wave.vcd
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#1000 $finish; // 一定要设置一个仿真停止时间
end
logic clk_a,clk_b,rstn,i_data,o_out;
initial begin
clk_a = 0;
forever begin
#10 clk_a = ~clk_a;
end
end
initial begin
clk_b = 0;
forever begin
#16 clk_b = ~clk_b;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
#500 rstn = 0;
#15 rstn = 1;
end
initial begin
i_data = 1'd0;
#28 i_data = 1'b1;
#20 i_data = 1'b0;
// #20 i_data = 1'b0;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
end
onebit_sync_s2f u_onebit_sync_s2f(
.clk_a ( clk_a ),
.clk_b ( clk_b ),
.rstn ( rstn ),
.i_data ( i_data ),
.o_out ( o_out )
);
endmodule
仿真波形
7.边沿检测
// 信号边沿检测,采样频率要大于信号频率的两倍
module edge_detect (
input logic clk,
input logic rstn,
input logic i_data,
output logic pos_edge,
output logic neg_edge,
output logic data_edge
);
logic [1:0] data_r;
always @(posedge clk or negedge rstn) begin
if(!rstn)begin
data_r <= 2'b0;
end
else begin
data_r <= {data_r[0],i_data};
end
end
assign pos_edge = ~data_r[1] & data_r[0];
assign neg_edge = data_r[1] & ~data_r[0];
assign data_edge = pos_edge | neg_edge;
endmodule
测试文件
`timescale 1ns/1ps
module tb();
// iverilog -g2005-sv -o sim.out tb.sv edge_detect.sv
// vvp -n sim.out
// gtkwave wave.vcd
initial begin
$dumpfile("wave.vcd"); // 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
//$dumpfile("./simulation/wave.vcd"); // wave.vcd文件将存储在当前目录下的simulation文件夹下
$dumpvars(0, tb ); // 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
#1000 $finish; // 一定要设置一个仿真停止时间
end
logic clk,rstn,i_data,pos_edge,neg_edge,data_edge;
initial begin
clk = 0;
forever begin
#10 clk = ~clk;
end
end
initial begin
rstn = 1;
#15 rstn = 0;
#10 rstn = 1;
#500 rstn = 0;
#15 rstn = 1;
end
initial begin
i_data = 1'd0;
#28 i_data = 1'b1;
#45 i_data = 1'b0;
#45 i_data = 1'b1;
#45 i_data = 1'b0;
#45 i_data = 1'b1;
#45 i_data = 1'b1;
#45 i_data = 1'b0;
// #20 i_data = 1'b0;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
// #20 i_data = 1'b1;
// #20 i_data = 1'b0;
// #20 i_data = 1'b1;
end
edge_detect u_edge_detect(
.clk ( clk ),
.rstn ( rstn ),
.i_data ( i_data ),
.pos_edge ( pos_edge ),
.neg_edge ( neg_edge ),
.data_edge ( data_edge )
);
endmodule
仿真波形
8.异步fifo
// 异步FIFO,使用格雷码
module FIFO_async
#(
parameter DWIDTH = 8,
parameter AWIDTH = 3,
parameter DDEPTH = 8
)
(
input logic rstn,
input logic w_clk,
input logic w_en,
input logic [DWIDTH-1:0] w_data,
input logic r_clk,
input logic r_en,
output logic [DWIDTH-1:0] r_data,
output logic w_full,
output logic r_empty
);
// 读写地址
logic [AWIDTH-1:0] w_addr,r_addr;
// 读写指针,比读写地址多一位,用来表示是否在同一轮中
logic [AWIDTH:0] w_ptr,r_ptr;
// 读写指针的格雷码
logic [AWIDTH:0] w_ptr_gray,r_ptr_gray;
// 写指针格雷码同步到读时钟域,读指针格雷码同步到写时钟域
logic [AWIDTH:0] w_ptr_gray_to_r [1:0];
logic [AWIDTH:0] r_ptr_gray_to_w [1:0];
// 定义fifo的memory
logic [DWIDTH-1:0] fifo_mem [DDEPTH-1:0];
//----------------------------------------------------------------------
// 写指针递增
always @(posedge w_clk or negedge rstn) begin
if(!rstn)begin
w_ptr <= 'd0;
end
else begin
w_addr <= (w_en == 1'b1 & !w_full) ? (w_addr + 1'b1) : w_addr;
end
end
// 将写指针转换为格雷码
assign w_ptr_gray = (w_addr>>1) ^ w_addr;
// 生成写地址
assign w_addr = w_ptr[AWIDTH-1:0];
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// 读指针递增
always @(posedge w_clk or negedge rstn) begin
if(!rstn)begin
r_ptr <= 'd0;
end
else begin
r_addr <= (r_en == 1'b1 & !r_empty) ? (r_addr + 1'b1) : r_addr;
end
end
// 将读指针转换为格雷码
assign r_ptr_gray = (r_addr>>1) ^ r_addr;
// 生成读地址
assign r_addr = r_ptr[AWIDTH-1:0];
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// 写指针格雷码同步到读时钟域
always@(posedge r_clk or negedge rstn)begin
if(!rstn)begin
w_ptr_gray_to_r[0] <= 'd0;
w_ptr_gray_to_r[1] <= 'd0;
end
else begin
w_ptr_gray_to_r = {w_ptr_gray_to_r[0] , w_ptr_gray};
end
end
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// 读指针格雷码同步到写时钟域
always@(posedge r_clk or negedge rstn)begin
if(!rstn)begin
r_ptr_gray_to_w[0] <= 'd0;
r_ptr_gray_to_w[1] <= 'd0;
end
else begin
r_ptr_gray_to_w = {r_ptr_gray_to_w[0] , r_ptr_gray};
end
end
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//判断写满信号,前两位都不同,后面位都相同
assign w_full = (r_ptr_gray)==({~w_ptr_gray_to_r[1][AWIDTH-:2] , w_ptr_gray_to_r[1][AWIDTH-2:0]});
//判断读空信号,前两位都不同,后面位都相同
assign r_empty = (w_ptr_gray == r_ptr_gray_to_w[1]);
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// 写入和读出memory
always@(posedge w_clk)begin
fifo_mem[w_addr] <= ((w_en == 1'b1) & !w_full) ? w_data : fifo_mem[w_addr];
end
always@(posedge r_clk)begin
r_data <= ((r_en == 1'b1) & !r_empty) ? fifo_mem[r_addr] : r_data;
end
//----------------------------------------------------------------------
endmodule
9.同步fifo
//单口RAM实现同步fifo
module singleRAM_fifo#(
parameter DSIZE = 32,
parameter ASIZE = 3,
parameter DDEPTH = 8
)
(
input logic clk,
input logic rstn,
input logic wen,
input logic [DSIZE-1:0] wdata,
input logic ren,
output logic [DSIZE-1:0] rdata,
output logic rempty,
output logic wfull
);
// 读写指针
logic [ASIZE:0] wptr,rptr;
// 读写地址
logic [ASIZE-1:0] waddr,raddr;
// RAM地址输入
logic [ASIZE-1:0] RAM_i_addr;
// RAM数据输入
logic [DSIZE-1:0] RAM_i_data;
// 写指针递增
always_ff@(posedge clk or negedge rstn)begin
if(!rstn)begin
wptr <= 0;
end
else if(wen & !wfull)begin
wptr <= wptr + 1'b1;
end
else begin
wptr <= wptr;
end
end
// 读指针递增
always_ff@(posedge clk or negedge rstn)begin
if(!rstn)begin
rptr <= 0;
end
else if(ren & !rempty)begin
rptr <= rptr + 1'b1;
end
else begin
rptr <= rptr;
end
end
// 写地址
assign waddr = wptr[ASIZE-1:0];
// 读地址
assign raddr = rptr[ASIZE-1:0];
// 读空信号
assign rempty = wptr == rptr;
// 写满信号
assign wfull = (wptr[ASIZE-1:0] == rptr[ASIZE-1:0]) & (wptr[ASIZE]^rptr[ASIZE]);
// RAM地址信号
assign RAM_i_addr = wen?wdata:rdata;
singleRAM RAM1(
.clk(clk),
.i_data(wdata),
.i_addr(RAM_i_addr),
.oe(ren & !rempty),
.wr(wen & !wfull),
.o_data(rdata)
);
endmodule
module singleRAM #(
parameter DSIZE = 32,
parameter ASIZE = 3,
parameter DDEPTH = 8
)
(
input logic clk,
input logic [DSIZE-1:0] i_data,
input logic [ASIZE-1:0] i_addr,
input logic oe, // 允许输出线
input logic wr, // 写使能线
output logic [DSIZE-1:0] o_data
);
logic [DSIZE-1:0] mem [DDEPTH-1:0];
assign o_data = oe?32'bz:mem[i_addr];
always_ff@(posedge clk )begin
if(wr)
mem[i_addr] <= i_data;
end
endmodule
10.如图
将两个乘法器减少为1个,同时数据选择器多用了1个;