|
module ex_case(
input wire rst_n,
input wire sclk,
output reg o_dv,//使能标志信号,标志数据有效
output reg [7:0] o_data,
//用于数据输入
input wire [9:0] i_data,
input wire [7:0] i_addr
);
reg [2:0] cnt_7;//计数器
//不同功能的寄存器分开always块来写,这样可维护性强,可读性强
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_7<=3'd0;
else
cnt_7<=cnt_7 + 1'b1;
always @(posedge sclk or negedge rst_n)//case语句只能在always块里面,case语句是并行语句,没有先后
if(rst_n == 1'b0)
begin
o_data<=8'd0;
o_dv<=1'b0;
end
else begin
case(cnt_7)
3'd0:begin
o_data<=3'd7;//当cnt_7等于3'd0执行此条语句,
o_dv<=1'b1;//reg的概念:上升沿前面的低电平把数据加载到D端,当上升沿来临的时候就把D端的数据呈现在Q端
end
3'd1:begin
o_data<=3'd0;//case语句相当于一个译码器,对应一个真值表(综合时生成LUT)
o_dv<=1'b0;
end
3'd2:begin
o_data<=3'd5;
o_dv<=1'b1;
end
default:begin
o_data<=3'd0;
o_dv<=1'b0;
end
endcase
end
//下列是组合逻辑实现
//消除锁存器:
//1.把敏感列表写全,case条件;赋值语句右边的变量;
//2.所有条件分支写全
//always @(cnt_7)//敏感列表是变量的时候是组合逻辑。是电平触发。敏感列表必须要完整,否则生成锁存器(只有电平触发的时候才有可能生成锁存器)。锁存器延迟的时间不固定还是电平触发,导致此路径不能参与到时序分析里边
// case(cnt_7)
// 3'd0:begin
// o_data<=3'd7;//当cnt_7等于3'd0执行此条语句
// o_dv<=1'b1;
// end
// 3'd1:begin
// o_data<=3'd0;//case语句相当于一个译码器,对应一个真值表(综合时生成LUT(查找表就是把一些真值表存在里面),真值表的条件当地址,真值表的输出结果作为输出)
// o_dv<=1'b0;
// end
// 3'd2:begin
// o_data<=3'd5;
// o_dv<=1'b1;
// end
// default:begin//可以直接加一个“;”结束
// o_data<=3'd0;
// 0_dv<=1'b0;
// end
// endcase
endmodule
/*
wire和reg的具体区别
在RTL图里就可以看到:
wire就是一根线
reg就是一个D触发器,是一个硬件的元件(也可以理解为一个模块)
FPGA的基本单元是LE,一个LE里面会有寄存器
*/12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
`timescale 1ns /1ns
module tb_ex_case;
reg sclk,rst_n;
wire [7:0] data;
wire dv;
reg [7:0] i_addr;
reg [9:0] i_data;
initial begin
sclk = 0;
rst_n = 0;
#200
rst_n = 1;
end
initial begin
i_data=0;
i_addr=0;
#500
send_data(255);
end
always #10 sclk <=~sclk;//周期20ns的时钟
ex_case ex_case_inst(
.rst_n (rst_n),
.sclk (sclk),
.o_dv (dv),
.o_data (data),
//用于数据输入
.i_data (i_data),
.i_addr (i_addr)
);
task send_data(len);//任务名字和长度,任务的声明,类似C语言
integer len,i;//变量声明区
begin//必须加begin和end
for(i=0;i<len;i=i+1)//循环语句
begin
@(posedge sclk);//for循环的时候一定要按照节拍来工作。如果注视掉本行,在波形图上i_data和i_addr都为零不动了。因为瞬间溢出成0
i_addr<=i[7:0];//可以用阻塞赋值也可以用非阻塞赋值。阻塞赋值就是直接赋过去,非阻塞赋值会延时一拍。
i_data<=i[7:0];//截取低8位
//用case语句产生一个协议,当地址为0时,i_data=0xff;
//当地址为1时,i_data=0x55;
//当地址为2时,i_data=0x00;
//其他地址全部保留赋值为0xff;
end
i_addr<=0;
i_data<=0;
end
endtask
endmodule
/*
如果去描述一个ram,地址不要用case语句来描述,要用存储器变量,存储器变量使用reg来声明的。
单片机或arm这一类的芯片要和FPGA通信的时候,会有一个并行总线,并行总线给过来地址,可以用case来做
就相当于arm芯片来读取FPGA的一些寄存器的时候就可以用case语句来做
|
|