最近开始入门FPGA,断断续续做了些笔记。
今天装了lattice开发环境,尝试模仿历程,把LED_shining 历程敲一遍,进行编译。报错: 
提示为23行有语法错误,原因是module最后一个参数不需要逗号 
改掉这里再编译 提示25行错误 
定位实际是24行语句少写了分隔符导致,加上之后再编译就成功了。 
管脚分配,结合硬件手测分配管脚 两个输入 clk_in ,rst_n_in 两个输出 led1,led2 
 2019/2/28
三个开关控制RGB led实验。
module LED (sw,led);
input [2:0] sw;//开关输入信号,利用了其中3个开关
output [2:0] led;//输出信号到RGB LED
assign led = sw;/assign连续赋值。
endmodule
我想用3个开关控制两个RGB led灯: 
代码改为:
module RGB_LED1(sw,led1,led2);
input [2:0] sw;
output [2:0] led1;
output [2:0] led2;
assign led1 = sw;
assign led2 = sw;
endmodule
改为非阻塞赋值的方式:
需要注意的有两点,
1.输入port只能是wire型不能定义成reg型。
2.非阻塞赋值必须用在always块中执行
3.if 语句或者case 语句 后面只能跟一句赋值语句,若要跟多句,需要用到begin end语句
4.always块要定义触发信号
module RGB_LED(clk,rst,sw,led1,led2);
input clk,rst;
input [2:0] sw;
output led1,led2;
reg [2:0] led1;
reg [2:0] led2;
always@(posedge clk or negedge rst)
if(!rst)
begin
led1<=0;
led2<=0;
end
else
begin
led1<=sw;
led2<=sw;
end
endmodule
2019/3/1
开关控制数码管显示0~F;原例程为:
module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2);
input[3:0] seg_data_1;
input[3:0] seg_data_2;//小脚丫上第二个数码管
output[8:0] seg_led_1;//控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
output [8:0] seg_led_2;//在小脚丫上第二个数码管的控制信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
reg[8:0] seg [9:0];//定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽
initial//在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
//initial和always不同,其中语句只执行一次
begin
seg[0]=9'h3f;//对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0
seg[1]=9'h06;//7段显示数字 1
seg[2]=9'h5b;//7段显示数字 2
seg[3]=9'h4f;//7段显示数字 3
seg[4]=9'h66;//7段显示数字 4
seg[5]=9'h6d;//7段显示数字 5
seg[6]=9'h7d;//7段显示数字 6
seg[7]=9'h07;//7段显示数字 7
seg[8]=9'h7f;//7段显示数字 8
seg[9]=9'h6f;//7段显示数字 9
end
assign seg_led_1 = seg[seg_data_1];//连续赋值,这样输入不同四位数,就能输出对于译码的9位输出
assign seg_led_2 = seg[seg_data_2];
endmodule
原例程用的是数据流描述方式(assign)实现的,我改为行为描述方式(initial+always)
并从显示0~9扩展为显示0~F
注意的是 initial后面是不需要“:”
Initial 和always后都可以用非阻塞赋值,多个要用包含在begin...end 里。
module SHUMAGUAN(sw,key,led1,led2);
input [3:0] sw;
input [3:0] key;
output led1,led2;
reg [8:0] led1;
reg [8:0] led2;
reg [8:0] seg[16:0];
initial
begin
seg[0] <= 9'h0x3F;
seg[1] <= 9'h0x06;
seg[2] <= 9'h0x5B;
seg[3] <= 9'h0x4F;
seg[4] <= 9'h0x66;
seg[5] <= 9'h0x6D;
seg[6] <= 9'h0x7D;
seg[7] <= 9'h0x07;
seg[8] <= 9'h0x7F;
seg[9] <= 9'h0x6F;
seg[10]<= 9'h0x77;
seg[11]<= 9'h0x7C;
seg[12]<= 9'h0x39;
seg[13]<= 9'h0x5E;
seg[14]<= 9'h0x79;
seg[15]<= 9'h0x71;
end
always@(sw or key)
begin
case(sw)
4'b0000:led1 <= seg[0];
4'b0001:led1 <= seg[1];
4'b0010:led1 <= seg[2];
4'b0011:led1 <= seg[3];
4'b0100:led1 <= seg[4];
4'b0101:led1 <= seg[5];
4'b0110:led1 <= seg[6];
4'b0111:led1 <= seg[7];
4'b1000:led1 <= seg[8];
4'b1001:led1 <= seg[9];
4'b1010:led1 <= seg[10];
4'b1011:led1 <= seg[11];
4'b1100:led1 <= seg[12];
4'b1101:led1 <= seg[13];
4'b1110:led1 <= seg[14];
4'b1111:led1 <= seg[15];
default:led1 <= seg[0];
endcase
case(key)
4'b0000:led2 <= seg[0];
4'b0001:led2 <= seg[1];
4'b0010:led2 <= seg[2];
4'b0011:led2 <= seg[3];
4'b0100:led2 <= seg[4];
4'b0101:led2 <= seg[5];
4'b0110:led2 <= seg[6];
4'b0111:led2 <= seg[7];
4'b1000:led2 <= seg[8];
4'b1001:led2 <= seg[9];
4'b1010:led2 <= seg[10];
4'b1011:led2 <= seg[11];
4'b1100:led2 <= seg[12];
4'b1101:led2 <= seg[13];
4'b1110:led2 <= seg[14];
4'b1111:led2 <= seg[15];
default:led2 <= seg[0];
endcase
end
Endmodule
2019/3/5
按键消抖:
需要掌握两个概念:
脉冲边沿检测:用一个频率更高的时钟去触发待检测的信号,用两个寄存器去存储相邻两个时钟采集的值,然后进行异或运算,如果不为0,则代表了上升沿或者下降沿。
消抖方法,第一次检测脉冲,delay20ms,再检测key是否为低,如果为低则正面按键按下。否则认为是抖动。
我写的代码如下(有问题):
module key(clk,key,rst,led);
input key,rst,clk;
output reg led;
reg [17:0] count;
wire key_edge,key_pulse;
reg key_pre,key_now;
reg key2_pre,key2_now;
always@(posedge clk or negedge rst) //检测第一次脉冲发生 key_edge
begin
if(!rst)
begin
key_now=1'b1;
key_pre=1'b1;
end
else
begin
key_now <= key;
key_pre <= key_now;
end
end
assign key_edge = key_pre&(~key_now); //按键按下产生一个周期正脉冲
always@(posedge clk or negedge rst) //定时器初始化
begin
if(!rst)
count<=18'h0;
else if(key_edge)
count<=18'h0;
else
count<=count+1'h1;
end
always@(posedge clk or negedge rst) //如果有按键按下,延时21ms 再检测一次key
begin
if(!rst)
begin
key2_now<=1'b1;
end
else if(count==18'h3ffff)
begin
key2_now<=key;
key2_pre<=key2_now; //这里写错了
end
end
//always@(posedge clk or negedge rst)
//begin
//if(!rst)
//key2_pre <=1'b1;
//else
//key2_pre <=key2_now;
//end
assign key_pulse = key2_pre&(~key2_now);
always@(posedge clk or negedge rst)
begin
if(!rst)
led<=1'b1;
else if(key_pulse)
led<=~led;
else
led<=led;
end
endmodule
以上代码运行后并没有得到我想要的结果,按键按下亮,松掉熄灭。原因是延时结束后key2_pre 不应该在这里赋值。因为下一个时钟clk进来时count已经不等于18‘h3ffff了,则不会进入赋值

需要改为:
always@(posedge clk or negedge rst) //如果有按键按下,延时21ms 再检测一次key
begin
if(!rst)
begin
key2_now<=1'b1;
end
else if(count==18'h3ffff)
begin
key2_now<=key;
end
end
always@(posedge clk or negedge rst)
begin
if(!rst)
key2_pre <=1'b1;
else
key2_pre <=key2_now;
end
这里比较难理解,key2_now 是等待21ms后真正的按键状态,而key2_pre是key2_now前一个周期的状态,即为高,所以这是如果真正按键按下了,会输出一个周期的高电平脉冲。如果检测到这个脉冲,则认为按键是真的按下了。
2019/4/9
通过移位寄存器 练习编写测试文件,并对仿真结果进行分析:
移位寄存器源码:clk 来一次,高位向低位移位,rst 时赋初值
module SHIFT(clk,rst,datain,dataout);
input clk,rst;
input [6:0] datain;
output dataout;
wire dataout;
reg [6:0] data;
always@(posedge clk)
if(!rst)
data <= datain;
else
begin
data[6] <= 1'b0;
data[5] <= data[6];
data[4] <= data[5];
data[3] <= data[4];
data[2] <= data[3];
data[1] <= data[2];
data[0] <= data[1];
end
assign dataout = data[0];
endmodule
测试用例:
测试需要给条件为 clk 翻转周期,rst 持续时间,以及rst 时给datain 赋的初值
`timescale 1ns/100ps //
module SHIFT_TEST;
reg clk,rst;
reg [6:0] datain;
wire dataout;
initial
begin
clk = 0;
rst = 1;
datain = 7'b1110101;
#50
rst = 0;
#100
rst = 1;
end
always #10 clk = ~clk;
SHIFT u1(
.clk(clk),
.rst(rst),
.datain(datain),
.dataout(dataout)
);
Endmodule
仿真结果如下: 
把clk 周期改为20ns 看起来两个图有差别,实际上是一样的,rst 变高后 clk第一个上一个升沿为移出的数据,从clk上升沿往前看一个周期,其实都是1,移出的数据为1010111, 实现了移位的功能。RST 为高的时候数据才是有效的,有意思的是在rst为低时,输出是保持赋值的最低位不变的。 
比如改赋值为1110100,clk 周期为20ns 波形如下: 
完整的Word格式文档51黑下载地址:
小脚丫FPGA学习笔记.docx
(572.01 KB, 下载次数: 22)
|