摘抄至数字芯片实验室
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 fen
outputreg coin_out , //0 --0 fen ;1 --5 fen
input drink_in , //0 --5 fen drink ;1 --10 fen drink
outputreg 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) begin
if(!rst_n) begin
current_state <= IDLE ;
end
elsebegin
current_state <= next_state ;
end
end
reg coin_in_reg ,drink_in_reg ;
always@(posedge clk ornegedge rst_n) begin
if(!rst_n) begin
coin_out <= 0 ;
drink_out <= 0 ;
coin_in_reg <= 0 ;
drink_in_reg <= 0 ;
end
elsebegin
case(current_state)
IDLE :begin
coin_out <= 0 ;
drink_out <= 0 ;
drink_in_reg <= 0 ;
if(button[0]) begin
coin_in_reg <= coin_in ;
end
elsebegin
coin_in_reg <= 0 ;
end
end
S0 :begin
coin_out <= 0 ;
drink_out <= 0 ;
if(button[1]) begin
drink_in_reg <= drink_in;
end
elsebegin
drink_in_reg <= 0 ;
end
end
S1 :begin
coin_out <= 0 ;
if(coin_in_reg>= drink_in_reg) drink_out <= drink_in_reg ;
else drink_out <= 1'b0 ;
end
S2 :begin
drink_out <= 0 ;
drink_in_reg <= 0 ;
if(button[0]) begin
coin_in_reg <= coin_in ;
end
elsebegin
coin_in_reg <= 0 ;
end
if(coin_in_reg> drink_in_reg) coin_out <= 1'b1 ;
else coin_out <= 1'b0 ;
end
endcase
end
end
always@(*) begin
next_state = IDLE ;
case(current_state)
IDLE : begin
if(button[0]) begin
next_state = S0 ;
end
elsebegin
next_state = IDLE ;
end
end
S0 : begin
if(button[1]) begin
next_state = S1 ;
end
end
S1 : begin
if(button[2]) begin
next_state = S2 ;
end
end
S2 : begin
if(button[0]) begin
next_state = S0 ;
end
end
endcase
end
endmodule
module machine_tb;
reg clk ;
reg rst_n ;
reg[2:0] button ;
reg coin_in ;
wire coin_out ;
reg drink_in ;
wire drink_out ;
initialbegin
clk = 0 ;
rst_n = 1 ;
button = 0 ;
coin_in = 0 ;
drink_in = 0 ;
#20 rst_n = 0 ;
#20 rst_n = 1 ;
#30
@(posedge clk ) #1
button[0] = 1 ;
coin_in = 1 ;
@(posedge clk ) #1
button[0] = 0 ;
coin_in = 0 ;
button[1] = 1 ;
drink_in = 0 ;
@(posedge clk ) #1
button[1] = 0 ;
drink_in = 0 ;
button[2] = 1 ;
@(posedge clk ) #1
button[2] = 0 ;
button[0] = 1 ;
coin_in = 1 ;
@(posedge clk ) #1
button[0] = 0 ;
coin_in = 0 ;
button[1] = 1 ;
drink_in = 1 ;
@(posedge clk ) #1
button[1] = 0 ;
drink_in = 0 ;
button[2] = 1 ;
end
always #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) begin
if(!rst_n) begin
current_state <= 0 ;
pulse_out <= 0 ;
end
elsebegin
current_state <= next_state ;
pulse_out <= pulse_out_reg ;
end
end
always@(*) begin
next_state = IDLE;
case(current_state)
IDLE : begin
if(sequence_in == 2'b10) next_state =S0;
pulse_out_reg = 0 ;
end
S0 : begin
if(sequence_in == 2'b11) next_state =S1;
pulse_out_reg = 0 ;
end
S1 : begin
if(sequence_in == 2'b00) next_state =S2;
pulse_out_reg = 0 ;
end
S2 : begin
if(sequence_in == 2'b10 || sequence_in == 2'b11) begin
next_state = S3;
pulse_out_reg = 1 ;
end
end
S3 : begin
if(sequence_in == 2'b10) next_state =S0;
else next_state =IDLE;
pulse_out_reg = 0 ;
end
endcase
end
endmodule
module sequence_checker_2bit_tb ;
reg clk ;
reg rst_n ;
reg[1:0] sequence_in ;
wire pulse_out ;
initialbegin
clk = 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 ;
end
always #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 ) begin
if(!rst_n) begin
second_reg <= 0 ;
end
elseif(second_set)second_reg <= second_set_value ;
elseif(second_reg == 59) second_reg<= 0;
else second_reg<= second_reg +1 ;
end
always@(posedge clk ornegedge rst_n ) begin
if(!rst_n) begin
minute_reg <= 0 ;
end
elseif(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 ;
end
always@(posedge clk ornegedge rst_n ) begin
if(!rst_n) begin
hour_reg <= 0 ;
end
elseif(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 ;
initialbegin
clk = 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 ) #1
hour_set = 1 ;
hour_set_value= 10 ;
minute_set = 1 ;
minute_set_value = 10 ;
second_set= 1 ;
second_set_value = 10 ;
@(posedge clk ) #1
hour_set = 0 ;
hour_set_value= 0 ;
minute_set = 0 ;
minute_set_value = 0 ;
second_set= 0 ;
second_set_value = 0 ;
end
always #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)
begin
check_sum_o<= 8'h0;
end
else
begin
check_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];
end
endmodule
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)
begin
check_sum_o= 8'h0;
end
else
begin
ccc = check_sum;
for(i=0;i<8;i=i+1)
begin
ccc ={ccc[6:0],1'b0}^({8{ccc[7]}} & 8'h31);
end
check_sum_o = ccc;
end
endmodule
其实也可以将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 ;
begin
ccc= value;
for(i=0;i<8;i=i+1)
begin
flag= ccc & 8'h80 ;
if(flag!= 0 ) ccc = (ccc <<1) ^ 8'h31 ;
elseccc = (ccc <<1) ;
end
cal_table_high_first= ccc;
end
endfunction
always @ (posedge clk or negedge rst_n)
if(!rst_n)
begin
check_sum_o= 8'h0;
end
else
begin
check_sum_o<= cal_table_high_first(check_sum) ;
end
endmodule
综上,loop1.v和loop2.v的主要贡献是解开了算法实现的if-else判断。至于loop3.v中,将C语言描述的功能封装成fucntion,直接单周期完成赋值的实现方式在逻辑综合后是否增加了if-else判断语句的硬件开销不在本文讨论范围内。
这和设计者施加的时序约束和综合工具算法有关