学习摘自数字芯片实验室
1、复数计算(a + bj)*(c + dj) = (ac – bd) + (bc + ad)j,需要四个乘法器才能在一个时钟周期里计算完成,如何优化才能只需要三个乘法器在一个时钟周期里计算完成。
思路1:
ac -bd = (a – b)d + (c – d)a
ad+ bc = (a - b)d + (c + d)b
(a- b)d可以复用
思路2:
ac-bd =a(c-d)+d(a-b)
bc+ad =c(a+b)-a(c-d)
a(c-d)可以复用
和思路1和思路2相似的,还有很多
思路3:
bc + ad = (a+b)(c+d) – ac-bd
ac和bd可以复用
思路4:
bc + ad = (a-b)(d-c) +ac+bd
ac和bd可以复用
和思路3和思路4相似的,还有很多
2、时钟输入clk_in, sel为时钟控制信号,sel=0输出clk_in, sel = 1 输出clk_i的四分频,要求异步复位,保持时钟信号的完整性。
module clk_div(
input clk_in,
input rst_n,
output clk_div4
);
reg[1:0] cnt;
always@ (posedge clk_in or negedge rst_n)
if (~rst_n) begin
cnt <= 2'b0;
end else begin
cnt <= cnt + 1'b1;
end
assign clk_div4 = cnt[1];
endmodule
module clk_switch(
input wire clk_a,
input wire clk_b,
input wire rst_n,
input wire sel ,
output wire clk_o
);
// variable declaration
reg clk_a_en ;
reg clk_b_en ;
// logic
always @(negedge clk_a or negedge rst_n) begin
if(~rst_n) clk_a_en <= 1'b0 ;
else clk_a_en <= ~sel & ~clk_b_en ;
end
always @(negedge clk_b or negedge rst_n) begin
if(~rst_n) clk_b_en <= 1'b0 ;
else clk_b_en <= sel & ~clk_a_en ;
end
assign clk_o = (clk_a & clk_a_en) | (clk_b &clk_b_en) ;
endmodule
module clk_switch_top(
input clk_in,
input sel,
input rst_n,
output clk_o
);
wire clk_b;
clk_div clk_div_inst(
.clk_in(clk_in),
.rst_n(rst_n),
.clk_div4(clk_b)
);
clk_switch clk_switch_inst(
.clk_a(clk_in),
.clk_b(clk_b),
.rst_n(rst_n),
.sel(sel),
.clk_o(clk_o)
);
endmodule
Testbench:
module clk_switch_tb;
reg clk_in ;
reg rst_n ;
reg sel ;
wire clk_o ;
always #3 clk_in = ~clk_in;
initial
begin
clk_in = 0;
sel = 0;
rst_n = 1;
#10
rst_n = 0;
#7
rst_n = 1;
#10
sel = 1;
#10
sel = 0;
#37
sel = 1;
end
clk_switch_top top_inst(
.clk_in(clk_in),
.sel(sel),
.rst_n(rst_n),
.clk_o(clk_o)
);
endmodule
需要注意的是,需要避免开关过程中产生毛刺,需要使用下降沿驱动。使用上升沿驱动的话,结果会如下图所示:(箭头处出现毛刺)
3、一个16bit序列,每个clk左移一位,要求检测5的倍数。
思路:把这个16bit数看作是4个16进制数
那么假设该数为ABCD,则这个数字10进制为A163+B*162+C161+D*160,可以展开为A*(15+1)3+B*(15+1)2+C*(15+1)+D,移除能被5整除的项→A+B+C+D,所以只需要判断A+B+C+D能否被5整除。
module five(
input clk,
input rst_n,
input a,
output reg b
);
reg [15:0] seq;
always @ (posedge clk or negedgerst_n)
if (!rst_n)
seq <= 16'b0;
else
begin
seq <= {seq[14:0],a};
b <=(((seq[15:12]+seq[11:8]+seq[7:4]+seq[3:0])%5)==0);
end
endmodule
module five_tb;
reg clk;
reg rst_n;
reg a;
wire b;
always #1 clk=~clk;
initial
begin
clk=0;
rst_n=1;
a=0;
#4
rst_n=0;
#4
rst_n=1;
#2
a=0;
#2
a=1;
#2
a=0;
#2
a=1;
#2
a=0;
#30
$stop;
end
five five_inst(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b)
);
endmodule
原题是:一个16bit序列,每个clk左移一位,要求检测5的倍数。
前面的第二种思路
可以使用一个状态机实现。
s0表示余0,s1表示余1,s3表示余3,s4表示余4
一开始16bit全为0,所以是s0状态(余0),如果下一bit进来1,那就是16’h0001,就是s1状态(余1),如果下一bit进来0,那就是16’h0002,就是s2状态(余2),以此类推。只有在s0状态,out输出1
five_2.v:
module five_2(
input clk,
input rst_n,
input a,
output reg b
);
reg[15:0] seq;
rega_reg ;
always@ (posedge clk or negedge rst_n) begin
if (!rst_n)
a_reg <= 0;
else
begin
a_reg <= a;
end
end
always@ (posedge clk or negedge rst_n) begin
if (!rst_n)
seq <= 16'b0;
else
begin
seq <= {seq[14:0],a_reg};
end
end
parameterS0 = 3'b000,S1 = 3'b001,S2 = 3'b010,S3 = 3'b011,S4 = 3'b100 ;
reg[2:0] current_state,next_state ;
always@(posedgeclk or negedge rst_n) begin
if(!rst_n) begin
current_state <= S0 ;
b <= 1'b0 ;
end
else begin
current_state <= next_state ;
if(current_state == S0) begin
b <= 1 ;
end
else begin
b <= 0 ;
end
end
end
always@(*)begin
case(current_state)
S0:begin
if(a == 1'b1) next_state = S1 ;
else next_state = S0 ;
end
S1:begin
if(a == 1'b1) next_state = S3 ;
else next_state = S2 ;
end
S2:begin
if(a == 1'b1) next_state = S0 ;
else next_state = S4 ;
end
S3:begin
if(a == 1'b1) next_state = S2 ;
else next_state = S1 ;
end
S4:begin
if(a == 1'b1) next_state = S4 ;
else next_state = S3 ;
end
endcase
end
endmodule
使用之前同样的Testbench