题目:
设计一组合逻辑电路,将9位二进制数输入B8B7……B1B0转为用8421BCD码表示的3位十进制数,并将上述8421BCD码接入3个显示译码器驱动3个8段LED数码管,从而显示3位十进制数
解决:
分两步解决,第一步实现进制的转换,第二步实现LED数码管显示;并且两步骤均在SparkRoad板子上实现。
进制的转换
针对第一步,因为输入量较大,使用case显然较为繁琐,继而使用普遍的处理方法——移位加3法。
module shuzhizhuanhuan
(
input[8:0]bin4,
output reg[11:0] bcd );
reg[20:0]x;
integer i;
always@*
begin
for(i=0;i<21;i=i+1) //将每一位都赋予初值
begin
x[i]=0;
end
x[11:3]=bin4;
repeat(6)
begin
if(x[12:9]>4)
x[12:9]=x[12:9]+4'd3;
if(x[16:13]>4)
x[16:13]=x[16:13]+4'd3;
x[20:1]=x[19:0];
end
bcd=x[20:9];
end
endmodule
需要注意的点:
1.for循环严谨性
erilog的for循环中需要对齐或者添加begin与end,否则会导致软件的误识别
2.repeat循环次数
程序中6次是基于草稿推算。该程序将input和output连接,形成一个20位的数串(因为9位二进制最大的111111111转化得到的是10100010001,所以我才用了12位去表示,即010100010001),input从低位[8:0]依次向高位移位,而这种移位仅需要6次
3.移位加3法在本题中的改动
对于移位加3法,核心是对于个十百(更大的可以有千位)位,同时去判断每一位是否大于4,若大于4,则相应位加3,但是对于加完之后的数,不用再去判断是否大于4,例如:个位为0101,大于4,需加3,为1000,即可,不用继续对1000判断!CSDN相关二进制转BCD码的Verilog实现 并且,111111111对应的8421BCD码的百位为0101,这是符合BCD码的表达规则,但是并不需要上述的判断加3的!所以在程序中,并不需要对于百位也设置if语句。
仿真验证:

选取了511(010100010001)和287(001010000111)来验证,成功
引脚设置:

动态验证:

输入为111111111,输出010100010001

输入为000001100,输出为000000010010

输入为011010100(212),输出为001000010010
LED显示管实现
LED管显示如果使用静态LED,对于3个需要的LED则需要3x8共24个output,这在SparkRoad板子上无法实现。SparkRoad使用的是动态LED显示,由DIG_4,DIG_3,DIG_2,DIG_1来选取千百十个位(此题中并不需要千位了),由SEG_A到SEG_H来控制每个LED的8个显示灯管,因为动态显示的移动速度很快,肉眼无法区别,所以看上去是不变的了。
由于在第一部分中已经实现了数制转换功能,所以在这部分需要将数制转换代码与SparkRoad的LED代码结合。
先来看一下SparkRoad的LEDdemo代码,如下:
`timescale 1ns/ 1ps
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
//
// Author: Anlogic
//
// Description:
//
// 数码管 显示
//
// Web: www.anlogic.com
// --------------------------------------------------------------------
module seg4
#(
parameter CNT_TIME = 2400_000 //0.1s
)
(
input wire clk_24m,
output wire [7:0] sm_seg,
output wire [3:0] sm_bit
);
wire rst_n;
reg [24:0] cnt;
reg [3:0] addr;
reg [3:0] sm_bit1_num;
reg [3:0] sm_bit2_num;
reg [3:0] sm_bit3_num;
reg [3:0] sm_bit4_num;
//计数器,约为10ms扫描一次
reg [17:0] cnt_w;
//数码管位选
reg [3:0] sm_bit_reg;
reg [3:0] sm_seg_num ;
reg [7:0] sm_seg_reg;
localparam
S0 = 4'b0000 ,
S1 = 4'b0001 ,
S2 = 4'b0010 ,
S3 = 4'b0011 ,
S4 = 4'b0100 ,
S5 = 4'b0101 ,
S6 = 4'b0110 ,
S7 = 4'b0111 ,
S8 = 4'b1000 ,
S9 = 4'b1001 ;
rst_n ux_rst
(
.clk (clk_24m ),
.rst_n (rst_n )
);
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt <= 0;
else if(cnt == CNT_TIME)//0.1Hz
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit1_num <= 4'h0;
else if(cnt == CNT_TIME)//0.1Hz
begin
if(sm_bit1_num == 9)
sm_bit1_num <= 4'h0;
else
sm_bit1_num <= sm_bit1_num + 1'b1;
end
else
sm_bit1_num <= sm_bit1_num;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit2_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit1_num == 9)
begin
if(sm_bit2_num == 9 )
sm_bit2_num <= 4'h0;
else
sm_bit2_num <= sm_bit2_num + 1;
end
else
sm_bit2_num <= sm_bit2_num;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit3_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit2_num == 9 && sm_bit1_num == 9)
begin
if(sm_bit3_num == 9 )
sm_bit3_num <= 4'h0;
else
sm_bit3_num <= sm_bit3_num + 1;
end
else
sm_bit3_num <= sm_bit3_num;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit4_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit3_num == 9 && sm_bit2_num == 9 && sm_bit1_num == 9)
begin
if(sm_bit4_num == 9)
sm_bit4_num <= 4'h0;
else
sm_bit4_num <= sm_bit4_num + 1;
end
else
sm_bit4_num <= sm_bit4_num;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt_w <= 18'd0;
else if(cnt_w == 18'b111_111_111_111_111_111)
// else if(&cnt_w)
cnt_w <= 18'd0;
else
cnt_w <= cnt_w + 1;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_seg_num <= 4'h0;
else
begin
case( cnt_w[17:16] )
2'b00:sm_seg_num <= sm_bit1_num;
2'b01:sm_seg_num <= sm_bit2_num;
2'b10:sm_seg_num <= sm_bit3_num;
2'b11:sm_seg_num <= sm_bit4_num;
endcase
end
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit_reg <= 4'b1111;
else
begin
case( cnt_w[17:16] )
2'b00:sm_bit_reg <= 4'b1110;
2'b01:sm_bit_reg <= 4'b1101;
2'b10:sm_bit_reg <= 4'b1011;
2'b11:sm_bit_reg <= 4'b0111;
endcase
end
end
always@(*)
begin
case ( sm_seg_num )
S0:
sm_seg_reg <= 8'hc0;
S1:
sm_seg_reg <= 8'hf9;
S2:
sm_seg_reg <= 8'ha4;
S3:
sm_seg_reg <= 8'hb0;
S4:
sm_seg_reg <= 8'h99;
S5:
sm_seg_reg <= 8'h92;
S6:
sm_seg_reg <= 8'h82;
S7:
sm_seg_reg <= 8'hf8;
S8:
sm_seg_reg <= 8'h80;
S9:
sm_seg_reg <= 8'h90;
default:sm_seg_reg <= 8'hc0;
endcase
end
assign sm_seg = sm_seg_reg;
assign sm_bit = sm_bit_reg;
endmodule
该代码在Verimake论坛可以自行查找,实现了四位LED从0000到9999的循环递增,由reg [3:0] sm_bit1_num、
reg [3:0] sm_bit2_num、reg [3:0] sm_bit3_num、reg [3:0] sm_bit4_num来控制,在上述代码中可以自行查找。
而为了实现本题目的数值转换显示功能,则仅需要对上述demo代码中的reg [3:0] sm_bit1_num、reg [3:0] sm_bit2_num、reg [3:0] sm_bit3_num、reg [3:0] sm_bit4_num的4个always模块进行改动,改动后代码如下:
`timescale 1ns/ 1ps
module shuzhizhuanhuan_LED
#(
parameter CNT_TIME = 2400_000 //0.1s
)
(
input wire clk_24m,
output wire [7:0] sm_seg,
output wire [3:0] sm_bit, //LED显示器的设置
input[8:0]bin4,
output reg[11:0] bcd //数制转换的设置
);
wire rst_n;
reg [24:0] cnt;
reg [3:0] addr;
reg [3:0] sm_bit1_num;
reg [3:0] sm_bit2_num;
reg [3:0] sm_bit3_num;
reg [3:0] sm_bit4_num;
//计数器,约为10ms扫描一次
reg [17:0] cnt_w;
//数码管位选
reg [3:0] sm_bit_reg;
reg [3:0] sm_seg_num ;
reg [7:0] sm_seg_reg;
localparam
S0 = 4'b0000 ,
S1 = 4'b0001 ,
S2 = 4'b0010 ,
S3 = 4'b0011 ,
S4 = 4'b0100 ,
S5 = 4'b0101 ,
S6 = 4'b0110 ,
S7 = 4'b0111 ,
S8 = 4'b1000 ,
S9 = 4'b1001 ;
rst_n ux_rst
(
.clk (clk_24m ),
.rst_n (rst_n )
);
reg[20:0]x; //数制转换的设置
integer i;
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt <= 0;
else if(cnt == CNT_TIME)//0.1Hz
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
always@* //数制转换代码插入
begin
for(i=0;i<21;i=i+1)
begin
x[i]=0;
end
x[11:3]=bin4;
repeat(6)
#20
begin
if(x[12:9]>4)
x[12:9]=x[12:9]+4'd3;
if(x[16:13]>4)
x[16:13]=x[16:13]+4'd3;
x[20:1]=x[19:0];
end
bcd=x[20:9];
end
//将bcd与LED进行匹配
always@(posedge clk_24m or negedge rst_n) //分别对sm_bit1_num进行配置
begin
sm_bit1_num=bcd[3:0];
end
always@(posedge clk_24m or negedge rst_n)
begin
sm_bit2_num=bcd[7:4];
end
always@(posedge clk_24m or negedge rst_n)
begin
sm_bit3_num=bcd[11:8];
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt_w <= 18'd0;
else if(cnt_w == 18'b111_111_111_111_111_111)
// else if(&cnt_w)
cnt_w <= 18'd0;
else
cnt_w <= cnt_w + 1;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_seg_num <= 4'h0;
else
begin
case( cnt_w[17:16] )
2'b00:sm_seg_num <= sm_bit1_num;
2'b01:sm_seg_num <= sm_bit2_num;
2'b10:sm_seg_num <= sm_bit3_num;
2'b11:sm_seg_num <= sm_bit4_num;
endcase
end
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit_reg <= 4'b1111;
else
begin
case( cnt_w[17:16] )
2'b00:sm_bit_reg <= 4'b1110;
2'b01:sm_bit_reg <= 4'b1101;
2'b10:sm_bit_reg <= 4'b1011;
2'b11:sm_bit_reg <= 4'b0111;
endcase
end
end
always@(*)
begin
case ( sm_seg_num )
S0:
sm_seg_reg <= 8'hc0;
S1:
sm_seg_reg <= 8'hf9;
S2:
sm_seg_reg <= 8'ha4;
S3:
sm_seg_reg <= 8'hb0;
S4:
sm_seg_reg <= 8'h99;
S5:
sm_seg_reg <= 8'h92;
S6:
sm_seg_reg <= 8'h82;
S7:
sm_seg_reg <= 8'hf8;
S8:
sm_seg_reg <= 8'h80;
S9:
sm_seg_reg <= 8'h90;
default:sm_seg_reg <= 8'hc0;
endcase
end
assign sm_seg = sm_seg_reg;
assign sm_bit = sm_bit_reg;
endmodule
需要注意的点:
1.模块的补充
在demo代码中有一个模块如下:
rst_n ux_rst
(
.clk (clk_24m ),
.rst_n (rst_n )
);
需要在自己的project中同时添加demo代码中的另一个rst.v文件,否则会报错black block(虽然这个黑盒的具体影响我也不太清楚,听老师说是TD版本的问题)
2.代码的融合
对于output的bcd与LED的output对应问题,我采用的是如下写法:
always@(posedge clk_24m or negedge rst_n)
begin
sm_bit2_num=bcd[7:4];
end
(对于这个写法,自己还有些疑惑,觉得可以优化代码)
引脚设置:

动态验证:
输入为511,LED译码器显示管和led小灯(仅用来表示0、1)结果正确

输入为14,LED译码器显示管和led小灯(仅用来表示0、1)结果正确

输入为46,LED译码器显示管和led小灯(仅用来表示0、1)结果正确

总结与反思:
1.移位加3法的掌握
二进制转BCD码的移位加3法的窍门要熟悉,网上的诸多资料给出的都是八位二进制转换的实例,那么处理此题时,就要注意长字符串(我使用的用一个长字符串同时包含input和output)的向量位置问题,容易粗心出错;并且对于九位输入的独特性,在百位并不需要再次判断,对repeat次数也要清晰认识
2.TangDesitiny的掌握
本文代码的验证基于安路科技的TangDynasty,for循环的空格问题需要注意(后续准备在别的平台上再跑一下试试,测试一下空格会不会影响)
3.对于SparkRoad的LED使用还会在后续的学习过程中进一步体会
4.验证时需要给所有位赋予初值0,否则出现不定态
参考资料:
CSDN相关二进制转BCD码的Verilog实现
Modelsim波形仿真指南
SparkRoad数码管显示教程
SparkRoad基本功能电灯教程