关于组合逻辑时序逻辑,<=,=,电平触发和边缘触发
时序逻辑用<=(非阻塞赋值)
组合逻辑用=(阻塞赋值)
组合逻辑一般采用电平触发:always @ *
时序逻辑一般采用时钟边沿触发:always @( posedge clk)
暂存问题:reg型变量和wire型变量与上面的关系
1.非阻塞赋值和阻塞赋值与是否是reg型变量和wire型变量无关?
关于利用verilog将二进制码转换为十进制BCD码
思路
题目的关键在于状态机的设计,由于以输入数据为状态,将会导致状态太多,因此用等待不同的按键输入为状态,总共有下面四种状态:
1.清零状态:按下reset键会进入这种状态
2.等待加号或等号状态:按下confirm键会进入这种状态
3.等待加号或确认状态:按下equal键会进入这种状态
4.等待确认状态:按下add键会进入这种状态
加法的思路:
设计一个长度为8的向量作为最终的结果存放,设计两个长度为4的数组,分别存放的数的十位和个位数
显示的思路:
由于显示得数的两个数码管共用同一管脚,但是两个分别显示十位和个位,所以不能同时显示,只能沿用实验三的思路,同一时刻只显示一个数码管,但是变换频率很高,利用人眼的暂留效应,看起来是两个数码管同时显示。
输出的思路:
调用实验一中的数码管数字显示模块。
第二版成功程序
module shiyan4c(
input clk,
//input rst_n,
//四个按键
input clear_button,
input confirm_button,
input plus_button,
input equal_button,
//输入加数
input [3:0] number,
//输出结果 8位8421数
output [7:0] out,
output reg [1:0] wei
);
reg [7:0] result;
//暂存结果
reg [7:0] result_temp;
reg [3:0] input_temp;
reg [7:0] plus_result_temp;
//此变量仅用于在摁下等号之后,再摁确认键将暂存结果清零,进行新一轮加法
reg if_clear_result;
reg [3:0] ishi;reg [3:0] ige;//十位数和个位数
//用于频闪
localparam N = 18;
reg [N-1:0] regN;
reg [3:0] i;
initial begin
result_temp = 8'b0;
plus_result_temp = 8'b0;
input_temp = 4'b0;
end
//定义状态
parameter w_f_p_e = 2'b00; //处于等待加号或等号状态 wait for plus or equal
parameter w_f_c = 2'b01; //处于等待确认键状态 wait for confirm
parameter w_f_p_c = 2'b10; //处于等待加号或确认键状态 wait for plus or confirm
parameter clear = 2'b11; //清零状态 clear
reg [1:0] current_state;
reg [1:0] next_state;
//状态寄存器
always @(posedge clk) begin
if (clear_button)
current_state <= clear;
else
current_state <= next_state;
end
//次态的组合逻辑
always @* begin
case (current_state)
clear: begin
if (confirm_button) next_state = w_f_p_e;
else next_state = clear;
end
w_f_p_e: begin
if (plus_button) next_state = w_f_c;
else if (equal_button) next_state = w_f_p_c;
else next_state = w_f_p_e;
end
w_f_c: begin
if (confirm_button) next_state = w_f_p_e;
else next_state = w_f_c;
end
w_f_p_c:begin
if (plus_button) next_state = w_f_c;
else if (confirm_button) next_state = w_f_p_e;
else next_state = w_f_p_c;
end
default: next_state = 2'bxx;
endcase
end
//input_temp
always @(posedge clk) begin
if (current_state == clear)
input_temp <= 4'b0;
else if (current_state == w_f_p_e && confirm_button) //只有在wfpe状态时更新的input_temp才有效
input_temp <= number;
else if (current_state == w_f_p_c && equal_button) //按下等号后input_temp要清零
input_temp <= 4'b0;
end
//if_clear_result
always @ * begin
if (current_state == w_f_p_c && confirm_button) //在等号之后摁下确认键,且状态切换前
if_clear_result = 1'b1;
else
if_clear_result = 1'b0;
end
/*
关于plus_result_temp和result_temp:
加法的基本公式是:当前结果=输入+上次运算的结果
在本题中不能用result_temp <= result_temp + ...的形式,因为result_temp是时序逻辑,写成自加会每一个上升沿都进行计算。
试着用另一个变量去储存"上次运算的结果",因为输入是比较简单的。plus_result_temp就是这个功能。
因为按下确认键之后,进入wfpe状态,即下一个有效的键一定是加号或者等号,所以下一步一定是计算操作(除去清零,清零的优先级最高),
因此在wfpe状态就将下一步的结果计算出来,在wfpe的第一个周期input_temp还没变,但input_temp早晚会成为正确值,因此plus_result_temp
也是早晚会成为有效值。而plus_result_temp = result_temp +input_temp,即上次的结果和当前输入之和。
在摁下加号或者等号之后,进入的wfc或wfpc状态后把plus_result_temp赋给result_temp,这时的result_temp就是我们希望的值,并且在
此后,result_temp又成为了"上次运算的结果",供下次plus计算所用。当然,plus在wfc和wfpc的状态中是不能变更的。
*/
//plus_result_temp
always @(posedge clk) begin
if (current_state == clear)
plus_result_temp <= 8'b0;
else if (if_clear_result == 1'b1)
plus_result_temp <= 8'b0;
else if (current_state == w_f_p_e) begin
if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 > 4'b1001)) //这是加数达到三位数的情况,例如99+9
plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0110_0110;
else if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 <= 4'b1001)) //这是需要进位但加数不到三位数的情况
plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0000_0110;
else
plus_result_temp <= result_temp + {4'b0000,input_temp};
end
end
//result_temp
always @(posedge clk) begin
if (current_state == clear)
result_temp <= 8'b0;
else if (if_clear_result == 1'b1)
result_temp <= 8'b0;
//在输入加号或者等号的时候才进行计算,但加号只在wfc状态有效,等号只在wfpc状态有效
else if (current_state == w_f_c && plus_button)
result_temp <= plus_result_temp;
else if (current_state == w_f_p_c && equal_button)
result_temp <= plus_result_temp;
end
//输出result
always @ * begin
if (current_state == clear)
result = 8'b0;
else if (current_state == w_f_p_e)
result = {4'b0000,input_temp};
else if (current_state == w_f_c || current_state == w_f_p_c)
result = result_temp;
else
result = 8'b0;
//赋值给十位和个位
ishi=result[7:4];
ige=result[3:0];
end
//频闪
always @(posedge clk)
regN<= regN + 1;
always@ *
case (regN[N-1])
1'b0:begin
wei=2'b01;
i=ige;
end
1'b1:begin
wei=2'b10;
i=ishi;
end
endcase
//实例化
qiduanyimaqi A1(i,out);
endmodule // calculate
module qiduanyimaqi(
input [3:0] in,
output reg [7:0] out //in和out是子模块里面的输出,不对应管脚
);
always@*
begin
case(in)
4'b0000:out=8'b11111100;
4'b0001:out=8'b01100000;
4'b0010:out=8'b11011010;
4'b0011:out=8'b11110010;
4'b0100:out=8'b01100110;
4'b0101:out=8'b10110110;
4'b0110:out=8'b10111110;
4'b0111:out=8'b11100000;
4'b1000:out=8'b11111110;
4'b1001:out=8'b11100110;
4'b1010:out=8'b00011010;
4'b1011:out=8'b00110010;
4'b1100:out=8'b01000110;
4'b1101:out=8'b10010110;
4'b1110:out=8'b00011110;
4'b1111:out=8'b00000000;
endcase
end
endmodule
管脚设置
第一版失败程序
module shiyan4(
input clk,reset,
input [0:3] in,//每次的输入数据 !!!注意:这才是向量的命名,把[3:0]放后面是数组!!!
input add,equal,confirm,
output [7:0]ge,[7:0]shi,//个位和十位数码管的八个灯管的输出
output reg g=1,//个位所在的灯的公共端
output reg s=1//十位所在的灯的公共端
);
//以下为中间变量设置
localparam[1:0]
in1=2'b00,
in2=2'b01,
in3=2'b10;//三种状态
reg [1:0] inn,inst;//inn为当前状态,inst为下一个状态
reg [0:3] ige; reg [0:3] ishi;//个位数和十位数
reg [0:6] i=7'b0000000;//总的计算结果,由于题目要求不超过100,因此7位够用
always @(posedge reset)
if(reset) begin
inn<=in1;
i<=7'b0000000;
end
else
inn<=inst;
always @(posedge clk)
case(inn)
in1:
if(confirm)
inst<=in1;
else if(add)
inst<=in2;
else if(equal)
inst<=in3;
in2:
if(confirm)
inst<=in2;
else if(add)
inst<=in1;
else if(equal)
inst<=in3;
in3:
if(add)
inst<=in1;
endcase
always @(posedge clk)
case(inn)
in1:
if(confirm)
i<=in;//能这么写吗?
else if(add)
i<=in;
else if(equal)
i<=i+in;
in2:
if(confirm)
i<=in;//是不是此时已经更新到in2?
else if(add)
i<=i+in;
else if(equal)
i<=i+in;
in3:
if(add)
i<=i+in;
endcase
//假设此已经计算出最终的结果存在i里面
//Verilog里面只能对2的指数次幂做取模运算,
//因为对于2的指数次幂来说,只涉及到对寄存器中数据的左移右移操作,适合数字处理。
//如果在FPGA里要进行对任何整数的取模运算,对于确定的数,
//可以用while判断循环,转换成乘法和减法运算;
//对于不确定的数,由于while条件语句里不能有不确定项,所以只能转换成以最大循环次数为固定次数的for循环。
always @(*)
while (i>=10)begin
i=i-7'b0001010;
ishi=ishi+4'b0001;
ige = i[3:6];//把剩下的i(小于10)赋值给个位
end
qiduanyimaqi A1(ige,ge);
qiduanyimaqi A2(ishi,shi);
endmodule
module qiduanyimaqi(
input [0:3] in,
output reg [7:0] out //in和out是子模块里面的输出,不对应管脚
);
always@*
begin
case(in)
4'b0000:out=8'b00111111;
4'b0001:out=8'b00000110;
4'b0010:out=8'b01011011;
4'b0011:out=8'b01001111;
4'b0100:out=8'b01100110;
4'b0101:out=8'b01101101;
4'b0110:out=8'b01111101;
4'b0111:out=8'b00000111;
4'b1000:out=8'b01111111;
4'b1001:out=8'b01100111;
4'b1010:out=8'b01011000;
4'b1011:out=8'b01001100;
4'b1100:out=8'b01100010;
4'b1101:out=8'b01101001;
4'b1110:out=8'b01111000;
4'b1111:out=8'b00000000;
endcase
end
endmodule
//疑点1:in在时时更新,这里所有的in1 in2 ....都用in?
//疑点2:状态转换图正确吗?
//还是上次的问题:【0:3】还是【3:0】参考实验2
//目前改动:把中间计算用到的都改成0开头,输出正常
//尝试把十进制转化为二进制失败程序
// always @(*)
// while (i>=10)begin
// i=i-7'b0001010;
// ishi=ishi+4'b0001;
// ige = i[3:6];//把剩下的i(小于10)赋值给个位
// end
// qiduanyimaqi A1(ige,ge);
// qiduanyimaqi A2(ishi,shi);