1.VL59 根据RTL图编写Verilog程序
1.题目:
根据以下RTL图,使用 Verilog HDL语言编写代码,实现相同的功能,并编写testbench验证功能
2.解题思路
2.1 了解D触发器的知识 (在时钟是上升沿的时候, 输入是什么输出什么)
2.2 注意经过D触发器的器件需要 延时一个周期。
链接:时序约束系列之D触发器原理和FPGA时序结构 - 知乎 (zhihu.com)
3.解题代码
`timescale 1ns/1ns
module RTL(
input clk,
input rst_n,
input data_in,
output reg data_out
);
reg flag;
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
flag<=0;
end
else
begin
flag <=data_in ;
end
end
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
data_out<=0;
end
else
begin
data_out <= ~flag && data_in ;
end
end
endmodule
2.VL60 使用握手信号实现跨时钟域数据传输
1.题目:
分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加必要的握手信号,保证数据传输不丢失。
模块的接口信号图如下:
data_req和data_ack的作用说明:
data_req表示数据请求接受信号。当data_out发出时,该信号拉高,在确认数据被成功接收之前,保持为高,期间data应该保持不变,等待接收端接收数据。
当数据接收端检测到data_req为高,表示该时刻的信号data有效,保存数据,并拉高data_ack。
当数据发送端检测到data_ack,表示上一个发送的数据已经被接收。撤销data_req,然后可以改变数据data。等到下次发送时,再一次拉高data_req。
2.解题思路
2.1 两个部分一个接收端, 一个发送端。
2.2 两个部分的需求分析
2.3 开始 使用计时器 启动 发送部分的 data_req 信号。
2.4 信号需要延时两个周期。
3.解题代码
`timescale 1ns/1ns
module data_driver(
input clk_a,
input rst_n,
input data_ack,
output reg [3:0]data,
output reg data_req
);
reg data_ack_r1, data_ack_r2;
reg [2:0] cnt;
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
begin
data_ack_r1 <= 1'b0;
data_ack_r2 <= 1'b0;
end
else
begin
data_ack_r1 <= data_ack;
data_ack_r2 <= data_ack_r1;
end
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
data <= 4'b0;
else if(data_ack_r1 && !data_ack_r2)
data <= data + 1'b1;
else
data <= data;
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
cnt <= 1'b0;
else if (data_ack_r1 && !data_ack_r2)
cnt <= 1'b0;
else if (data_req) //正在请求时不计数
cnt <= cnt;
else
cnt <= cnt + 1'b1;
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
data_req <= 1'b0;
else if (cnt == 3'd4)
data_req <= 1'b1;
else if (data_ack_r1 && !data_ack_r2)
data_req <= 1'b0;
else
data_req <= data_req;
endmodule
module data_receiver(
input clk_b,
input rst_n,
output reg data_ack,
input [3:0]data,
input data_req
);
reg [3:0]data_in_reg;
reg data_req_1, data_req_2;
always @ (posedge clk_b or negedge rst_n)
if (!rst_n) begin
data_req_1 <= 1'b0;
data_req_2 <= 1'b0;
end
else begin
data_req_1 <= data_req;
data_req_2 <= data_req_1;
end
always @ (posedge clk_b or negedge rst_n)
if (!rst_n) begin
data_ack <= 1'b0;
data_in_reg <= 4'b0;
end
else if (data_req_1 && !data_req_2) begin
data_ack <= 1'b1;
data_in_reg <= data;
end
else begin
data_ack <= 1'b0;
data_in_reg <= data_in_reg;
end
endmodule
3. VL61 自动售卖机
1. 题目
请设计状态机电路,实现自动售卖机功能,A饮料5元钱,B饮料10元钱,售卖机可接收投币5元钱和10元钱,每次投币只可买一种饮料,考虑找零的情况。
电路的接口如下图所示。sel信号会先于din信号有效,且在购买一种饮料时值不变。
- sel为选择信号,用来选择购买饮料的种类,sel=0,表示购买A饮料,sel=1,表示购买B饮料;
- din表示投币输入,din=0表示未投币,din=1表示投币5元,din=2表示投币10元,不会出现din=3的情况;
- drinks_out表示饮料输出,drinks_out=0表示没有饮料输出,drinks_out=1表示输出A饮料,drinks_out=2表示输出B饮料,不出现drinks_out =3的情况,输出有效仅保持一个时钟周期;
- change_out表示找零输出,change_out=0表示没有找零,change_out=1表示找零5元,输出有效仅保持一个时钟周期。
接口电路图如下:
2.解题思路
2.1 首先 注意这个题有一点细节, 在买 B 饮料的时候, 可以存钱。(我去他的)。
2.1.1 在第一次 5 元 存入 , 第二次 5元 输出饮料。零钱清零。
2.1.2 在第一次 5元 存入 , 第二次 10元 输出饮料。 零钱输出 5 元。
2.1 信号延时 1 个周期输出。 (这是这个周期是 符合条件, 下一个 周期输出信号)。
3.解题代码
`timescale 1ns/1ns
module sale(
input clk ,
input rst_n ,
input sel ,//sel=0,5$dranks,sel=1,10&=$drinks
input [1:0] din ,//din=1,input 5$,din=2,input 10$
output reg [1:0] drinks_out,//drinks_out=1,output 5$ drinks,drinks_out=2,output 10$ drinks
output reg change_out
);
reg money;
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
drinks_out <=0;
change_out <=0;
money <=0;
end
else
begin
if(sel == 1'b1)
begin
if(din == 2'd1)//题目没有说可以存钱啊!1
begin
drinks_out <=0;
change_out <=1'd0; //连续输入两次5 元也可以
money <=1;
if(money)
begin
drinks_out <=2'd2;
change_out <=1'd0;
money <=0;
end
end
else if(din == 2'd2)
begin
drinks_out <=2'd2;
change_out <=0;
if(money)
begin
drinks_out <=2'd2;
change_out <=1'd1;
money <=0;
end
end
else
begin
drinks_out <=0;
change_out <=0;
end
end
else if(sel == 1'b0)
begin
if(din == 2'd1)
begin
drinks_out <=2'd1;
change_out <=1'd0;
end
else if(din == 2'd2)
begin
drinks_out <=2'd1;
change_out <=1'd1;
end
else
begin
drinks_out <=0;
change_out <=0;
end
end
end
end
endmodule
4.VL62 序列发生器
1.题目:
编写一个模块,实现循环输出序列001011。
模块的接口信号图如下:
2. 解题思路
2.1 第一种设置 参数 flag 001011 直接左移 输出 flag[5 ].
2.2 设置标志位 对应输出。
2.3 注意信号延时一个 周期。
3.解题代码
`timescale 1ns/1ns
module sequence_generator(
input clk,
input rst_n,
output reg data
);
parameter s0 = 3'd0;
parameter s1 = 3'd1;
parameter s2 = 3'd2;
parameter s3 = 3'd3;
parameter s4 = 3'd4;
parameter s5 = 3'd5;
reg[2:0] flag;
//需要延后一个周期 与
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
flag <= s0;
end
else
begin
case(flag)
s0 : flag <=s1;
s1 : flag <=s2;
s2 : flag <=s3;
s3 : flag <=s4;
s4 : flag <=s5;
s5 : flag <=s0;
default:flag <=s0;
endcase
end
end
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
data <=0;
end
else
begin
case (flag)
s0: data <= 0;
s1: data <= 0;
s2: data <= 1;
s3: data <= 0;
s4: data <= 1;
s5: data <= 1;
endcase
end
end
endmodule