电路设计与状态机
-
FPGA的基础概念
Cyclone IV器件采用了M9K的嵌入式块RAM,即每个嵌入式存储器块的容量为9216bit。4个PLL,一个PLL可以最多支持5路输出。
cyclone IV E中,除了EP4CE6和EP4CE10两个容量等级的器件只含有两个PLL单元以外,其它容量的器件均含有4个PLL。
M9K存储器,见名思意,该存储器每个M9K,存储容量是9Kbit,
这些存储器可以配置为FIFO,RAM等
18*18硬件乘法器,这些硬件乘法器的输入位宽都是18位。
-
序列检测
功能:实现检测序列:Welcom
为什么这些资源为0呢,因为我们的输出,还没有被赋予值,这时候资源就是0.
好,下面我们来正式看如何检测序列;
module Sequence_detection
(
input clk,
input rst_n,
input data_valid,//信号输入有效标志信号
input [7:0]data,
output [3:0]num,//计数检测到多少个序列
output reg flag//检测完毕标志信号
);
reg [2:0] state=3'b0;
reg [3:0] counter=4'b0;
localparam CHECK_W=3'd0;
localparam CHECK_E=3'd1;
localparam CHECK_L=3'd2;
localparam CHECK_C=3'd3;
localparam CHECK_O=3'd4;
localparam CHECK_M=3'd5;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state<=CHECK_W;
counter<=4'b0;
flag<=1'b0;
end
else if(data_valid)
begin
case(state)
CHECK_W://检测W
begin
if(data=="w")
begin
state<=CHECK_E;
flag<=0;
end
else
begin
state<=CHECK_W;
flag<=0;
end
end
CHECK_E://检测e
begin
if(data=="e")
state<=CHECK_L;
else if(data=="w")
state<=CHECK_E;
else
state<=CHECK_W;
end
CHECK_L://检测l
begin
if(data=="l")
state<=CHECK_C ;
else if(data=="w")
state<=CHECK_E;
else
state<=CHECK_W;
end
CHECK_C://检测c
begin
if(data=="c")
state<=CHECK_O;
else if(data=="w")
state<=CHECK_E;
else
state<=CHECK_W;
end
CHECK_O://检测o
begin
if(data=="o")
state<=CHECK_M;
else if(data=="w")
state<=CHECK_E;
else
state<=CHECK_W;
end
CHECK_M://检测m
begin
if(data=="m")
begin
counter<=counter+1;
flag<=1'b1;
state<=CHECK_W;
end
else if(data=="w")
state<=CHECK_E;
else
state<=CHECK_W;
end
default: state<=CHECK_W;
endcase
end
else
begin
state<=CHECK_W;
end
end
assign num=counter;
endmodule
测试文件testbench:
`timescale 1 ns/ 1 ns
`define clk_period 20
module Sequence_detection_vlg_tst();
reg clk;
wire [7:0] data;
reg data_valid;
reg [511:0] reg_shift;
reg rst_n;
// wires
wire flag;
wire [3:0]num;
// assign statements (if any)
Sequence_detection i1 (
.clk(clk),
.data(data),
.data_valid(data_valid),
.flag(flag),
.num(num),
.rst_n(rst_n)
);
initial
begin
clk=1;
end
always #(`clk_period/2) clk=~clk;
wire [511:0] data_to_send ;
assign data_to_send="whwecomdwhusardwhuwelcomwhusdardwwelcomdwhusdardwhusdardwhusdard";
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
reg_shift<=512'd0;
else if (data_valid)
reg_shift<=reg_shift<<8;
else
reg_shift<=data_to_send;
end
assign data=(data_valid==1)?reg_shift[511:504]:8'bz;
initial
begin
rst_n=0;
data_valid=0;
#(`clk_period*20);
rst_n=1;
#(`clk_period*20);
data_valid=1;
#(`clk_period*64);
data_valid=0;
#(`clk_period*10);
$stop;
end
endmodule
- 解说部分
- 设计实例部分
首先,代码不用解释了,入了门的,都看得懂,下面说说一些注意的地方:
3. 快捷键的使用:ctrl+F查找
ctrl+H替换
4. 报告没有消耗逻辑资源,说明输出端口没有被赋予值。
5.
状态机每一个都返回W状态和E状态,为什么会返回E状态呢?
因为,比如WELWELCOM,如果在L状态了,发现下一个状态是W,不是我们要的C,于是,下个时钟,跳转到W状态,但此时,在W状态,就会检测是否此状态是否是E,发现不符合,就会出现漏检,因为出现一个跳转时钟,耽误了,所以导致出现漏检。(因为我们送来检测的时序是,一个时钟,检测一次)
所以,我们在每个状态,我们还要检测一下是否此字母是W,如果是W,就跳转到E,避免出现漏检。
- 状态机跳转条件
我们可以看到L跳转到E的状态是1110111==h77
我们查一下ASCII ,正是我们小写的w
另外字也可以赋予到寄存器中,这也是我以前不太知道的,我以为只有数能赋予到寄存器中,字符根据ASCII码表。
我们对其是打双引号。data定义为8个位。
-
Testbench写法
宏定义
在module外定义宏 `define a 8 //无等号无分号使用时 (1) b<=`a +3; //用`a,不是a (2) `define b (`a+3) //用`a,不是a
此处我们宏定义时钟周期20ns
`define clk_period 20
我们在tb里面写的clk_period都写的是‘clk_period
输入都是reg???!!
对于我们这个也不是输入就是定死的reg类型,这也是一般初学者的误区。
其实我们写TB就是给输入写值,把它当成寄存器来写,所以我们都是把实例中的input,在TB里面写成reg,这样我们就能对其赋值。
但是,如果我们在TB里面,自己定义个别的寄存器,然后,assign到如上图所示的data,我们就不需要非得把data变成reg
initial的写法
其他就按照always@(posedge or negedge)来写即可
wire也可赋予初值
modisim遇见no design不要怕
选中simulate就可以查看到TB的语法错误了。
另外最后提醒一点,更改了顶层文件的输入输出端口过后,一定要记得更改TB的输入输出端口。