1 多路选择器
1.1 MUX21
代码:
//MUX21
module MUX
(
IN0,IN1,SEL,OUT
);
parameter WL = 16; // 输入输出数据信号位宽
input [WL-1:0] IN0, IN1;// 选择器的两个输入数据信号
input SEL; // 通道选通的控制信号
output[WL-1:0] OUT; // 选择器的输出数据信号
reg [WL-1:0] OUT;
always @(IN0 or IN1 or SEL) begin
if(SEL)
OUT=IN1;
else
OUT=IN0;
end
endmodule
RTL视图:
1.2 MUX41
代码:
//MUX41
module MUX41
(
IN0,IN1,IN2,IN3,SEL,OUT
);
parameter WL = 2; // 输入输出数据信号位宽
input [WL-1:0] IN0,IN1,IN2,IN3;// 选择器的两个输入数据信号
input SEL; // 通道选通的控制信号
output[WL-1:0] OUT; // 选择器的输出数据信号
reg [WL-1:0] OUT;
always @(IN0 or IN1 or IN2 or IN3 or SEL) begin
if(SEL==00)
OUT=IN0;
else if(SEL==01)
OUT=IN1;
else if(SEL==10)
OUT=IN2;
else
OUT=IN3;
end
endmodule
RTL视图:
与MUX21相比,资源的变化:
MUX21:
MUX41:
波形仿真:
2 交叉开关
2.1 2*2交叉开关
代码:
// module top, a 2x2 crossbar switch circuit
module two_two_crossbar_switch(
IN0 , // input 1
IN1 , // input 2
SEL0 , // select the output0 source
SEL1 , // select the output1 source
OUT0 , // output data 0
OUT1 ); // output data 1
parameter WL = 16;
input [WL-1:0] IN0, IN1;
input SEL0, SEL1;
output[WL-1:0] OUT0, OUT1;
reg [WL-1:0] OUT0, OUT1;
// get the OUT0
always @ (IN0 or IN1 or SEL0) begin
if(SEL0)
OUT0 = IN1;
else
OUT0 = IN0;
end
// get the OUT1
always @ (IN0 or IN1 or SEL1) begin
if(SEL1)
OUT1 = IN1;
else
OUT1 = IN0;
end
endmodule
// endmodule top
RTL视图:
资源消耗:
2.2 4*4交叉开关
代码:
module four_four_crossbar_switch
(IN0,IN1,IN2,IN3,SEL0,SEL1,SEL2,SEL3,SEL4,SEL5,SEL6,SEL7,OUT0,OUT1,OUT2,OUT3);
parameter WL = 16;
input [WL-1:0] IN0,IN1,IN2,IN3;
input SEL0,SEL1,SEL2,SEL3,SEL4,SEL5,SEL6,SEL7;
output [WL-1:0]OUT0,OUT1,OUT2,OUT3;
reg [WL-1:0]OUT0,OUT1,OUT2,OUT3;
always @ (IN0 or IN1 or IN2 or IN3 or SEL0 or SEL1)
begin
if (!SEL0)
if(!SEL1)
OUT0 = IN0;
else
OUT0 = IN1;
else
if(!SEL1)
OUT0 = IN2;
else
OUT0 = IN3;
end
always @ (IN0 or IN1 or IN2 or IN3 or SEL2 or SEL3)
begin
if(!SEL2)
if(!SEL3)
OUT1 = IN0;
else
OUT1 = IN1;
else
if(!SEL3)
OUT1 = IN2;
else
OUT1 = IN3;
end
always @ (IN0 or IN1 or IN2 or IN3 or SEL4 or SEL5)
begin
if(!SEL4)
if(!SEL5)
OUT2 = IN0;
else
OUT2 = IN1;
else
if(!SEL5)
OUT2 = IN2;
else
OUT2 = IN3;
end
always @ (IN0 or IN1 or IN2 or IN3 or SEL6 or SEL7)
begin
if(!SEL6)
if(!SEL7)
OUT3 = IN0;
else
OUT3 = IN1;
else
if(!SEL7)
OUT3 = IN2;
else
OUT3 = IN3;
end
endmodule
RTL视图:
资源消耗:
3 优先编码器
3.1 4输入优先编码器
// module top, 4 input priority encoder with zero input check
module top(
IN , // input
OUT ); // output
input [3:0] IN;
output[2:0] OUT;
reg [2:0] OUT;
// get the OUT
always @ (IN) begin
if(IN[3]) // 第一优先
OUT = 3'b011;
else if(IN[2]) // 第二优先
OUT = 3'b010;
else if(IN[1]) // 第三优先
OUT = 3'b001;
else if(IN[0]) // 第四优先
OUT = 3'b000;
else // 什么都没有检测到
OUT = 3'b111; // 输出值可自定义,不和上面的输出值混淆即可
end
endmodule
3.2 8输入优先编码器
// module top, 8 input priority encoder with zero input check
module eight_input_encoder(
IN , // input
OUT ); // output
input [7:0] IN;
output[3:0] OUT;
reg [3:0] OUT;
// get the OUT
always @ (IN) begin
if(IN[7])
OUT = 4'b0111;
else if(IN[6])
OUT = 4'b0110;
else if(IN[5])
OUT = 4'b0101;
else if(IN[4])
OUT = 4'b0100;
else if(IN[3])
OUT = 4'b0011;
else if(IN[2])
OUT = 4'b0010;
else if(IN[1])
OUT = 4'b0001;
else if(IN[0])
OUT = 4'b0000;
else // 什么都没有检测到
OUT = 4'b1111; // 输出值可自定义,不和上面的输出值混淆即可
end
endmodule
RTL视图:
4 多路译码器
4.1 3-8译码器
// module top, 4 input priority encoder with zero input check
module three_eight_decoder(
IN , // input
OUT ); // output
input [2:0] IN;
output[7:0] OUT;
reg [7:0] OUT;
// get the OUT
always @ (IN) begin
case(IN)
3'b000: OUT = 8'b0000_0001;
3'b001: OUT = 8'b0000_0010;
3'b010: OUT = 8'b0000_0100;
3'b011: OUT = 8'b0000_1000;
3'b100: OUT = 8'b0001_0000;
3'b101: OUT = 8'b0010_0000;
3'b110: OUT = 8'b0100_0000;
3'b111: OUT = 8'b1000_0000;
// full case 不需要写default,否则一定要有default
endcase
end
endmodule
资源消耗:
RTL视图:
4.2 4-16译码器
// module top, 4 input priority encoder with zero input check
module four_sixteen_decoder(
IN , // input
OUT ); // output
input [3:0] IN;
output[15:0] OUT;
reg [15:0] OUT;
// get the OUT
always @ (IN) begin
case(IN)
4'b0000: OUT = 16'b0000_0000_0000_0001;
4'b0001: OUT = 16'b0000_0000_0000_0010;
4'b0010: OUT = 16'b0000_0000_0000_0100;
4'b0011: OUT = 16'b0000_0000_0000_1000;
4'b0100: OUT = 16'b0000_0000_0001_0000;
4'b0101: OUT = 16'b0000_0000_0010_0000;
4'b0110: OUT = 16'b0000_0000_0100_0000;
4'b0111: OUT = 16'b0000_0000_1000_0000;
4'b1000: OUT = 16'b0000_0001_0000_0000;
4'b1001: OUT = 16'b0000_0010_0000_0000;
4'b1010: OUT = 16'b0000_0100_0000_0000;
4'b1011: OUT = 16'b0000_1000_0000_0000;
4'b1100: OUT = 16'b0001_0000_0000_0000;
4'b1101: OUT = 16'b0010_0000_0000_0000;
4'b1110: OUT = 16'b0100_0000_0000_0000;
4'b1111: OUT = 16'b1000_0000_0000_0000;
// full case 不需要写default,否则一定要有default
endcase
end
endmodule
资源消耗:
可以看出,比3-8译码器的资源消耗多了大概一倍。
RTL视图:
5 加法器
5.1 无符号加法器
module add(
IN1 ,
IN2 ,
OUT );
input[3:0] IN1, IN2;
output[4:0] OUT;
reg[4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
5.2 实验
- 把加法器的输出信号改成4比特位宽,编译,波形仿真。观察输出结果,说出输出和输入的对应关系。
module add_2(
IN1 ,
IN2 ,
OUT );
input[3:0] IN1, IN2;
output[3:0] OUT;//输出改为四位宽
reg[3:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
波形仿真:
分析:原例给出的输入信号为4位输入,数据范围在0-15,相加后范围在0-30,所以输出用5位可以表示。 当输入仍为4位而输出变为4位时,相加结果在大于15时会产生溢出,导致出错,仅能正确计算相加的后四位。
可以看到超过15的只能输出正确结果的后四位。
- 把加法器的输入信号改成8比特位宽,编译,波形仿真。观察加法器的输出延迟,和4比特输入位宽的情况对比,你有什么结论,为什么?
module add_3(
IN1 ,
IN2 ,
OUT );
input[7:0] IN1, IN2;//输入改为8位宽
output[4:0] OUT;
reg[4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
波形仿真:
分析:当输入变为8位时,输入的范围变为0-127,相加后的范围为0-254,而输出仍为5位,能表示的数据范围为0-31,所以只能正确输出结果的后五位。
5.3 补码加法器
module complement_add(
IN1 ,
IN2 ,
OUT );
input signed [3:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
实验:把加法器的输出信号改成4比特位宽,编译,波形仿真。观察输出结果,观察输出结果在什么时候是正确的?。 把加法器的输入信号改成8比特位宽,编译,波形仿真。观察加法器的输出延迟,和4比特输入位宽的情况对比,你有什么结论,为什么?
- 把加法器的输出信号改成4比特位宽
module complement_add_2(
IN1 ,
IN2 ,
OUT );
input signed [3:0] IN1, IN2;
output signed [3:0] OUT;
reg signed [3:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
波形仿真:
- 把加法器的输入信号改成8bit位宽
module complement_add_3(
IN1 ,
IN2 ,
OUT );
input signed [7:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 + IN2;
end
endmodule
波形仿真:
5.4 带流水线的加法器
每一个D触发器都有其所容许的最小的建立与保持时间,当两个D触发器之间的组合电路逻辑延迟变得更大的时候,会导致电路只能工作在更低的时钟频率,为了让电路能够工作在更高的时钟频率,需要用D触发器来把大块的组合逻辑分割为小块,这就是流水线技术。
module top(
IN1 ,
IN2 ,
CLK ,
OUT );
input [3:0] IN1, IN2;
input CLK;
output [4:0] OUT;
reg [3:0] in1_d1R, in2_d1R;
reg [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
in1_d1R <= IN1;
in2_d1R <= IN2;
OUT <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
adder_out = in1_d1R + in2_d1R;
end
endmodule
实验: 不改变流水线的级数,把加法器的输入信号改成8比特位宽,编译,波形仿真,和不带流水线的情况对比一下,你有什么结论? 在8比特输入位宽的情况下,在输入上再添加一级流水线,观察编译和仿真的结果,你有什么结论?
- 不改变流水线的级数,把加法器的输入信号改成8比特位宽
结论:与不加流水线的加法器比,带流水线的加法器由于加入了D触发器,有效地减少了组合逻辑的竞争与冒险,从而“毛刺”的长度明显减短。
module water_2(
IN1 ,
IN2 ,
CLK ,
OUT );
input [7:0] IN1, IN2;
input CLK;
output [4:0] OUT;
reg [7:0] in1_d1R, in2_d1R;
reg [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
in1_d1R <= IN1;
in2_d1R <= IN2;
OUT <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
adder_out = in1_d1R + in2_d1R;
end
endmodule
波形仿真:
RTL:
- 在8比特输入位宽的情况下,在输入上再添加一级流水线
结论: 流水线级数越高,毛刺越短,同时输出时延相应变长。
module water_3(
IN1 ,
IN2 ,
CLK ,
OUT );
input [7:0] IN1, IN2;
input CLK;
output [4:0] OUT;
reg [7:0] in1_d1R, in2_d1R;
reg [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
in1_d1R <= IN1;
in2_d1R <= IN2;
in1_d2r <=in1_d1r;
in2_d2r <=in2_d1r;
OUT <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
adder_out = in1_d2R + in2_d2R;
end
endmodule
6 乘法器
乘法器是一种奢侈品会消耗大量的组合电路逻辑资源,一定要慎重使用。尤其是对于芯片内部没有硬件乘法器的FPGA芯片,需要更加慎重。
无符号的乘法器 /
module top(
IN1 ,
IN2 ,
OUT );
input [3:0] IN1, IN2;
output [7:0] OUT;
reg [7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 * IN2;
end
endmodule
有符号的2补码乘法器 /
module top(
IN1 ,
IN2 ,
OUT );
input signed[3:0] IN1, IN2;
output signed [7:0] OUT;
reg signed[7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 * IN2;
end
endmodule
实验:
- 改变乘法器的输入位宽为8比特,编译,波形仿真,观察信号毛刺的时间长度。
module mul_2(
IN1 ,
IN2 ,
OUT );
input [7:0] IN1, IN2;
output [7:0] OUT;
reg [7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
OUT = IN1 * IN2;
end
endmodule
波形仿真:
-
选一款没有硬件乘法器的FPGA芯片(例如Cyclone EP1C6)对比8比特的乘法器和加法器两者编译之后的资源开销(Logic Cell的数目)
-
编写一个输入和输出都有D触发器的流水线乘法器代码,编译后波形仿真,观察组合逻辑延迟和毛刺的时间,和不带流水线的情况下对比。
波形仿真:
7 计数器
7.1 简单计数器
设计一个最简单的计数器,只有一个CLK输入和一个OVerflow输出,当计数到最大值的时钟周期CLK输出1。
//简单计数器
module simple_cnt(
CLK,
OV);
input CLK;
output OV;
reg[3:0] cnt_next;
reg OV;
parameter MAX_VAL=7;
always @ (posedge CLK) begin
if(cnt_next <MAX_VAL)begin
OV<=0;
cnt_next <=cnt_next+1;
end
else begin
OV <=1;
cnt_next <=0;
end
end
endmodule
RTL视图:
7.2 复杂计数器
复杂计数器代码 /
module top(
RST , // 异步复位, 高有效
CLK , // 时钟,上升沿有效
EN , // 输入的计数使能,高有效
CLR , // 输入的清零信号,高有效
LOAD , // 输入的数据加载使能信号,高有效
DATA , // 输入的加载数据信号
CNTVAL, // 输出的计数值信号
OV );// 计数溢出信号,计数值为最大值时该信号为1
input RST , CLK , EN , CLR , LOAD ;
input [3:0] DATA ;
output [3:0] CNTVAL;
output OV;
reg [3:0] CNTVAL, cnt_next;
reg OV;
// 电路编译参数,最大计数值
parameter CNT_MAX_VAL = 9;
// 组合逻辑,生成cnt_next
// 计数使能最优先,清零第二优先,加载第三优先
always @(EN or CLR or LOAD or DATA or CNTVAL) begin
if(CLR) begin
cnt_next = 0;
end
else begin
if(EN) begin
if(LOAD) begin
cnt_next = DATA;
end
else begin
if(CNTVAL < CNT_MAX_VAL) begin
cnt_next = CNTVAL + 1'b1;
end
else begin // 计数到最大值,下一计数值为0
cnt_next = 0;
end
end
end
else begin // 使能无效,计数值保持不动
cnt_next = CNTVAL;
end // else EN
end
end
always @ (posedge CLK or posedge RST) begin
if(RST)
CNTVAL <= 0;
else
CNTVAL <= cnt_next;
end
// 组合逻辑,生成OV
always @ (CNTVAL) begin
if(CNTVAL == CNT_MAX_VAL)
OV = 1;
else
OV = 0;
end
endmodule
RTL视图:
8 状态机
module state(
CLK,
RST,
CENT1IN,
TINOUT,
EN
);
input CLK;
input RST, EN;
input CENT1IN;
output TINOUT;
parameter ST_0_CENT = 0;
parameter ST_1_CENT = 1;
parameter ST_2_CENT = 2;
parameter ST_3_CENT = 3;
parameter ST_4_CENT = 4;
reg [3:0] stateR;
reg [3:0] next_state;
reg TINOUT;
always @ (CENT1IN or stateR)
begin
case (stateR)
ST_0_CENT :begin if (CENT1IN && EN) next_state = ST_1_CENT ; else next_state = ST_0_CENT; end
ST_1_CENT :begin if ((CENT1IN == 0) && EN) next_state = ST_2_CENT ; else next_state = ST_1_CENT; end
ST_2_CENT :begin if (CENT1IN && EN) next_state = ST_3_CENT ; else next_state = ST_0_CENT; end
ST_3_CENT :begin if (CENT1IN && EN) next_state = ST_4_CENT ; else next_state = ST_2_CENT; end
ST_4_CENT :begin if (CENT1IN && EN) next_state = ST_1_CENT ; else next_state = ST_2_CENT; end
default next_state = ST_0_CENT;
endcase
end
// calc output
always @ (stateR)
begin
if(stateR == ST_4_CENT)
TINOUT = 1'b1;
else
TINOUT = 1'b0;
end
// state DFF
always @ (posedge CLK or posedge RST)
begin
if(RST)
stateR <= ST_0_CENT;
else
stateR <= next_state;
end
endmodule
RTL:
9 移位寄存器
module shift
(
RST, //异步复位,高有效
CLK, //时钟,上升沿有效
EN, //输入数据串行移位使能
IN, //输入串行数据
OUT //并行输出数据
);
input RST, CLK, EN;
input IN;
output [3 : 0] OUT;
reg [3 : 0] shift_R;
assign OUT [3 : 0] = shift_R[3 : 0];
// shift_R 会被编译为D触发器
always @ (posedge CLK or posedge RST) //时钟,上升沿有效 异步复位,高有效 posedge从低到高 从高到低 都会触发
begin
if(RST) //异步复位,高有效
shift_R[3 : 0] <= 0;//清零
else if(EN) //异步复位,高有效
begin // 串行移位的使能有效
shift_R[3 : 1] <= shift_R[2 : 0];
shift_R[0] <= IN;
end
else
begin // 使能无效保持不动
shift_R[3 : 0] <= shift_R[3 : 0];
end
end // always
endmodule
RTL: