摘抄至数字芯片实验室
1、设计一个序列信号发生器电路,能在CLK信号作用下周期性输出“0010110111”的序列信号
2、设计一个自动饮料售卖机,共有两种饮料,其中饮料A每个10分钱,饮料B每个5分钱,硬币有5分和10分两种,并考虑找零。要求用状态机实现,定义状态,画出状态转移图,并用Verilog完整描述该模块
module machine(input clk ,input rst_n ,input[2:0] button, //button[0] --coin_in ;button[1] --drink_in ;button[2]-- coin_out input coin_in , //0 --5 fen ;1 --10 fenoutputreg coin_out , //0 --0 fen ;1 --5 feninput drink_in , //0 --5 fen drink ;1 --10 fen drinkoutputreg drink_out //0 --5 fen drink ;1 --10 fen drink);parameter[1:0] IDLE = 2'b00 , S0 = 2'b01 , S1 = 2'b10 , S2 = 2'b11 ;
reg[1:0] current_state, next_state ;always@(posedge clk ornegedge rst_n) beginif(!rst_n) begincurrent_state <= IDLE ;endelsebegincurrent_state <= next_state ;end
endreg coin_in_reg ,drink_in_reg ;
always@(posedge clk ornegedge rst_n) beginif(!rst_n) begincoin_out <= 0 ;drink_out <= 0 ;coin_in_reg <= 0 ;drink_in_reg <= 0 ;endelsebegincase(current_state)IDLE :begincoin_out <= 0 ;drink_out <= 0 ;drink_in_reg <= 0 ;if(button[0]) begincoin_in_reg <= coin_in ;endelsebegincoin_in_reg <= 0 ;endendS0 :begincoin_out <= 0 ;drink_out <= 0 ;if(button[1]) begindrink_in_reg <= drink_in;endelsebegindrink_in_reg <= 0 ;endendS1 :begincoin_out <= 0 ;if(coin_in_reg>= drink_in_reg) drink_out <= drink_in_reg ;else drink_out <= 1'b0 ;endS2 :begindrink_out <= 0 ;drink_in_reg <= 0 ;if(button[0]) begincoin_in_reg <= coin_in ;endelsebegincoin_in_reg <= 0 ;endif(coin_in_reg> drink_in_reg) coin_out <= 1'b1 ;else coin_out <= 1'b0 ;endendcaseend
endalways@(*) beginnext_state = IDLE ;case(current_state)IDLE : beginif(button[0]) beginnext_state = S0 ;endelsebeginnext_state = IDLE ;endendS0 : beginif(button[1]) beginnext_state = S1 ;endendS1 : beginif(button[2]) beginnext_state = S2 ;endendS2 : beginif(button[0]) beginnext_state = S0 ;endendendcase
endendmodule
module machine_tb;reg clk ;reg rst_n ;reg[2:0] button ;reg coin_in ;wire coin_out ;reg drink_in ;wire drink_out ;initialbeginclk = 0 ;rst_n = 1 ;button = 0 ;coin_in = 0 ;drink_in = 0 ;#20 rst_n = 0 ;#20 rst_n = 1 ;#30@(posedge clk ) #1button[0] = 1 ;coin_in = 1 ;@(posedge clk ) #1button[0] = 0 ;coin_in = 0 ;button[1] = 1 ;drink_in = 0 ; @(posedge clk ) #1button[1] = 0 ;drink_in = 0 ; button[2] = 1 ;@(posedge clk ) #1button[2] = 0 ;button[0] = 1 ;coin_in = 1 ;@(posedge clk ) #1button[0] = 0 ;coin_in = 0 ;button[1] = 1 ;drink_in = 1 ; @(posedge clk ) #1button[1] = 0 ;drink_in = 0 ; button[2] = 1 ;endalways #10 clk = ~clk;machine machine(.clk(clk) ,.rst_n(rst_n) ,.button(button) , .coin_in(coin_in) , .coin_out(coin_out) , .drink_in(drink_in) , .drink_out(drink_out) );endmodule
1、 用户必须按照投币-买饮料-找零的顺序依次操作
2、 每轮购买操作中,用户只能投币一次,不然会发生吞币!(哈哈~~~)
3、 投币大小不能比购买饮料的价格小
3、请实现对(1011001)2的序列检测功能,模块每拍并行输入2bit,且顺序为高位先输入,当检测到序列,输出一拍高电平脉冲。请用Verilog描述该模块。
相当于检测10_11_00_1X
module sequence_checker_2bit(input clk ,input rst_n,input[1:0] sequence_in ,outputreg pulse_out
);parameter[2:0] IDLE = 3'b000 ,S0= 3'b001,S1= 3'b010,S2= 3'b011,S3= 3'b100 ;reg[2:0] current_state,next_state ;reg pulse_out_reg ;
always@(posedge clk ornegedge rst_n) beginif(!rst_n) begincurrent_state <= 0 ;pulse_out <= 0 ;endelsebegincurrent_state <= next_state ;pulse_out <= pulse_out_reg ;end
endalways@(*) beginnext_state = IDLE;case(current_state)IDLE : beginif(sequence_in == 2'b10) next_state =S0;pulse_out_reg = 0 ;endS0 : beginif(sequence_in == 2'b11) next_state =S1;pulse_out_reg = 0 ;endS1 : beginif(sequence_in == 2'b00) next_state =S2;pulse_out_reg = 0 ;endS2 : beginif(sequence_in == 2'b10 || sequence_in == 2'b11) beginnext_state = S3;pulse_out_reg = 1 ;endendS3 : beginif(sequence_in == 2'b10) next_state =S0;else next_state =IDLE;pulse_out_reg = 0 ;endendcase
endendmodule
module sequence_checker_2bit_tb ;reg clk ;reg rst_n ;reg[1:0] sequence_in ;wire pulse_out ;initialbeginclk = 0 ;rst_n = 1 ;sequence_in = 2'b00 ;#20 rst_n = 0;#20 rst_n = 1 ;@(posedge clk) #1 sequence_in = 2'b10 ;@(posedge clk) #1 sequence_in = 2'b11 ;@(posedge clk) #1 sequence_in = 2'b00 ;@(posedge clk) #1 sequence_in = 2'b10 ;@(posedge clk) #1@(posedge clk) #1@(posedge clk) #1 sequence_in = 2'b10 ;@(posedge clk) #1 sequence_in = 2'b11 ;@(posedge clk) #1 sequence_in = 2'b00 ;@(posedge clk) #1 sequence_in = 2'b11 ;endalways #10 clk = ~clk;sequence_checker_2bitsequence_checker_2bit(.clk(clk) ,.rst_n(rst_n),.sequence_in(sequence_in) ,.pulse_out(pulse_out)
);endmodule
4、请基于f = 100Hz的Clock设计一个数字时钟,用Verilog实现以下功能
1、产生时、分、秒的计时2、可通过3个按键来设置时、分、秒值
module clock(input clk ,input rst_n ,input hour_set,input[4:0] hour_set_value,input minute_set,input[5:0]minute_set_value ,input second_set ,input [5:0]second_set_value ,output[5:0] second_out ,output[5:0] minute_out ,output[4:0] hour_out
);reg[5:0] second_reg ;reg[5:0] minute_reg ;reg[4:0] hour_reg ;always@(posedge clk ornegedge rst_n ) beginif(!rst_n) beginsecond_reg <= 0 ;endelseif(second_set)second_reg <= second_set_value ;elseif(second_reg == 59) second_reg<= 0;else second_reg<= second_reg +1 ;endalways@(posedge clk ornegedge rst_n ) beginif(!rst_n) beginminute_reg <= 0 ;endelseif(minute_set)minute_reg <= minute_set_value ;elseif(minute_reg == 59 &&second_reg == 59) minute_reg <= 0;elseif(second_reg == 59) minute_reg<= minute_reg +1 ;endalways@(posedge clk ornegedge rst_n ) beginif(!rst_n) beginhour_reg <= 0 ;endelseif(hour_set)hour_reg <= hour_set_value ;elseif(hour_reg == 23 &&minute_reg == 59 && second_reg == 59) hour_reg <= 0;elseif(minute_reg == 59 &&second_reg == 59 ) hour_reg <= hour_reg +1 ;end assign second_out= second_reg ;
assign minute_out= minute_reg;
assign hour_out = hour_reg;
endmodule
```c
module clock_tb ;reg clk ;reg rst_n ;reg hour_set ;reg[4:0] hour_set_value;reg minute_set ;reg[5:0]minute_set_value;reg second_set ;reg[5:0]second_set_value;wire[5:0]second_out ;wire[5:0]minute_out ;wire[4:0] hour_out ;initialbeginclk = 0 ;rst_n = 1 ;hour_set = 0 ;hour_set_value = 0;minute_set = 0 ;minute_set_value = 0;second_set = 0 ;second_set_value = 0;#10 rst_n = 0 ;#10 rst_n = 1 ;@(posedge clk ) #1hour_set = 1 ;hour_set_value= 10 ;minute_set = 1 ;minute_set_value = 10 ;second_set= 1 ;second_set_value = 10 ;@(posedge clk ) #1hour_set = 0 ;hour_set_value= 0 ;minute_set = 0 ;minute_set_value = 0 ;second_set= 0 ;second_set_value = 0 ;endalways #5 clk =~clk ;clock clock(.clk(clk) ,.rst_n(rst_n) ,.hour_set(hour_set) ,.hour_set_value(hour_set_value) ,.minute_set(minute_set),.minute_set_value(minute_set_value) ,.second_set(second_set),.second_set_value(second_set_value) ,.second_out(second_out) ,.minute_out(minute_out) ,.hour_out(hour_out)
);endmodule
5、矩阵键盘
参考文章:
https://www.cnblogs.com/yuphone/archive/2010/11/09/1783623.html
6、将一个串行执行的C语言算法转化为单拍完成的并行可综合verilog。
C语言源码如下:
unsignedcharcal_table_high_first(unsignedcharvalue)
{unsigned char i ;
unsigned char checksum = value ; for (i=8;i>0;--i){if (check_sum& 0x80){check_sum = (check_sum<<1) ^ 0x31;}else{check_sum = (check_sum << 1);}}return check_sum;
}
算法C语言实现:
#include<stdio.h>
int main(){unsignedchar cal_table_high_first(unsignedcharvalue); unsignedchar data;for (unsignedchar i = 0; i < 16;++i){data= cal_table_high_first(i);printf("value =0x%0x:check_sum=0x%0x \n", i, data);}getchar();
}unsignedchar cal_table_high_first(unsignedcharvalue)
{unsignedchar i;unsigned char check_sum = value;for (i = 8; i > 0;--i){if (check_sum &0x80){check_sum= (check_sum << 1) ^ 0x31;}else{check_sum= (check_sum << 1);}}return check_sum;
}
输出结果:
value =0x0:check_sum=0x0
value =0x1:check_sum=0x31
value =0x2:check_sum=0x62
value =0x3:check_sum=0x53
value =0x4:check_sum=0xc4
value =0x5:check_sum=0xf5
value =0x6:check_sum=0xa6
value =0x7:check_sum=0x97
value =0x8:check_sum=0xb9
value =0x9:check_sum=0x88
value =0xa:check_sum=0xdb
value =0xb:check_sum=0xea
value =0xc:check_sum=0x7d
value =0xd:check_sum=0x4c
value =0xe:check_sum=0x1f
value =0xf:check_sum=0x2e
C语言作为参考模型,用于后续Verilog的功能仿真。
该算法逻辑如下:
输入一个8bit的数,首先判断最高位是否为1,如果为1则左移一位,并且和8‘b00110001异或;如果最高位不为1则左移一位。此过程执行8次。
根据上述结果,可以用verilog描述。
module loop1(input clk,input rst_n,input [7:0] check_sum,output reg [7:0] check_sum_o
);
//reg [7:0] check_sum_o;
always @ (posedge clk or negedge rst_n)if(!rst_n)begincheck_sum_o<= 8'h0;endelsebegincheck_sum_o[7]<= check_sum[3]^check_sum[2]^check_sum[5];check_sum_o[6]<= check_sum[2]^check_sum[1]^check_sum[4]^check_sum[7];check_sum_o[5]<= check_sum[1]^check_sum[7]^check_sum[0]^check_sum[3]^check_sum[6];check_sum_o[4]<= check_sum[7]^check_sum[0]^check_sum[3]^check_sum[6];check_sum_o[3]<= check_sum[3]^check_sum[7]^check_sum[6];check_sum_o[2]<= check_sum[2]^check_sum[6]^check_sum[5];check_sum_o[1]<= check_sum[1]^check_sum[5]^check_sum[4]^check_sum[7];check_sum_o[0]<= check_sum[0]^check_sum[4]^check_sum[3]^check_sum[6];endendmodule
testbench
module loop1_tb;
reg clk;
reg rst_n;
reg [7:0] check_sum;
wire [7:0] check_sum_o;
always #1 clk=~clk;
initial
begin
clk = 0;
rst_n = 0;
#10
rst_n = 1;
for (check_sum=0;check_sum<16;check_sum=check_sum+1)begin#2//check_sum = i;$display ("check_sum = %h",check_sum_o);if (check_sum == 15) $stop;end
//$stop;
end
loop1 loop1_i1(.clk(clk),.rst_n(rst_n),.check_sum(check_sum),.check_sum_o(check_sum_o)
);
endmodule
loop2.v的实现和loop1.v类似,只是代码量更少。
module loop2(input clk,input rst_n,input [7:0] check_sum,output reg [7:0] check_sum_o
);
integer i;
//reg [7:0] check_sum_o;
reg [7:0] ccc;
always @ (posedge clk or negedge rst_n)if(!rst_n)begincheck_sum_o= 8'h0;endelsebeginccc = check_sum;for(i=0;i<8;i=i+1)beginccc ={ccc[6:0],1'b0}^({8{ccc[7]}} & 8'h31);endcheck_sum_o = ccc;endendmodule
其实也可以将C语言函数封装成Verilog的function,然后在在单周期内进行赋值。
module loop3(input clk,input rst_n,input [7:0] check_sum,output reg [7:0] check_sum_o);integer i;function [7:0] cal_table_high_first;input[7:0] value;reg[7:0] ccc ;reg[7:0] flag ;beginccc= value;for(i=0;i<8;i=i+1)beginflag= ccc & 8'h80 ;if(flag!= 0 ) ccc = (ccc <<1) ^ 8'h31 ;elseccc = (ccc <<1) ;endcal_table_high_first= ccc;endendfunctionalways @ (posedge clk or negedge rst_n)if(!rst_n)begincheck_sum_o= 8'h0;endelsebegincheck_sum_o<= cal_table_high_first(check_sum) ;endendmodule
综上,loop1.v和loop2.v的主要贡献是解开了算法实现的if-else判断。至于loop3.v中,将C语言描述的功能封装成fucntion,直接单周期完成赋值的实现方式在逻辑综合后是否增加了if-else判断语句的硬件开销不在本文讨论范围内。
这和设计者施加的时序约束和综合工具算法有关