FPGA程序设计

在设计FPGA时,多运用模块化的思想取设计模块,将某一功能设计成module。

设计之前要先画一下模块设计图,列出输入输出接口,再进一步设计内部功能。

状态机要画图,确定每个状态和状态之间怎么切换。状态用localparam定义。

顶层向下传递的要定义为wire,底层向上传递的要定义为reg。

根据时序将每一个时钟周期进行按步分解。对于通讯协议要对每条线仔细分析。

每个模块要用一个tb测试

testbench例子

cpp 复制代码
    always #10 Clk = ~Clk;
	
	initial begin
        Clk = 1;
	    Rst_n = 0;
	    Cmd = 6'b000000;
	    Go = 0;
	    Tx_DATA = 8'd0;
	    #2001;
		Rst_n = 1;
		#2000;
		
		//写数据操作,往EEPROM器件的B1地址写数据DA
		//第一次:起始位+EEPROM器件地址(7位)+写方向(1位)
		Cmd = STA | WR;
	    Go = 1;
	    Tx_DATA = 8'hA0 | 8'd0;//写方向
	    @ (posedge Clk);
        #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    //第二次:写8位EEPROM的寄存器地址
	    Cmd = WR;
	    Go = 1;
	    Tx_DATA = 8'hB1;//写地址B1
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    //第三次:写8位数据 + 停止位
	    Cmd = WR | STO;
	    Go = 1;
	    Tx_DATA = 8'hda;//写数据DA
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    #5000000; //仿真模型的两次操作时间间隔
	    //读数据操作,从EEPROM器件的B1地址读数据
	    //第一次:起始位+EEPROM器件地址(7位)+写方向(1位)
		Cmd = STA | WR;
	    Go = 1;
	    Tx_DATA = 8'hA0 | 8'd0;//写方向
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    //第二次:写8位EEPROM的寄存器地址
	    Cmd = WR;
	    Go = 1;
	    Tx_DATA = 8'hB1;//写地址B1
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    //第三次:起始位+EEPROM器件地址(7位)+读方向(1位)
		Cmd = STA | WR;
	    Go = 1;
	    Tx_DATA = 8'hA0 | 8'd1;//读方向
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    //第四次:读8位数据 + 停止位
	    Cmd = RD | STO;
	    Go = 1;
	    @ (posedge Clk);
	    #1;
	    Go = 0;
	    @ (posedge Trans_Done);
	    #200;
	    
	    #2000;
	    $stop;
	end

每个模块要建立一个testbench

时钟分频

复制代码
   	//系统时钟采用50MHz
	parameter SYS_CLOCK = 50_000_000;
	//SCL总线时钟采用400kHz
	parameter SCL_CLOCK = 400_000;
	//产生时钟SCL计数器最大值
	localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK/4 - 1;

    reg [19:0]div_cnt;
	reg en_div_cnt;
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		div_cnt <= 20'd0;
	else if(en_div_cnt)begin
		if(div_cnt < SCL_CNT_M)
			div_cnt <= div_cnt + 1'b1;
		else
			div_cnt <= 0;
	end
	else
		div_cnt <= 0;

	wire sclk_plus = div_cnt == SCL_CNT_M;

串转并

cpp 复制代码
reg[7:0]Rx_DATA;
Rx_DATA <= {Rx_DATA[6:0],i2c_sdat};

命令可以选择使用独热码编码

cpp 复制代码
localparam
IDLE = 7'b0000001, //空闲状态
GEN_STA = 7'b0000010, //产生起始信号
WR_DATA = 7'b0000100, //写数据状态
RD_DATA = 7'b0001000, //读数据状态
CHECK_ACK = 7'b0010000, //检测应答状态
GEN_ACK = 7'b0100000, //产生应答状态
GEN_STO = 7'b1000000; //产生停止信号

亚稳态

跨时钟域信号传输;异步信号采集;复位电路。

在FPGA系统中,如果数据传输中不满足触发器的Tsu和Th不满足,或者复位过程中复位信号的释放相对于有效时钟沿的恢复时间(recovery time)不满足,就可能产生亚稳态,此时触发器输出端Q在有效时钟沿之后比较长的一段时间处于不确定的状态,在这段时间里Q端在0和1之间处于振荡状态,而不是等于数据输入端D的值。这段时间称为决断时间(resolution time)。经过resolution time之后Q端将稳定到0或1上,但是稳定到0或者1,是随机的,与输入没有必然的关系。

异步信号采集

cpp 复制代码
always@(posedge clk or posedge reset)
    if(reset)begin
        uart_rx_sync1 <= 1'b0;
        uart_rx_sync2 <= 1'b0;
    end
    else begin
        uart_rx_sync1 <= uart_rx;
        uart_rx_sync2 <= uart_rx_sync1;
end

边沿检测

cpp 复制代码
//下降沿检测
always@(posedge clk or posedge reset)
if(reset)begin
    uart_rx_reg1 <= 1'b0;
    uart_rx_reg2 <= 1'b0;
end
else begin
    uart_rx_reg1 <= uart_rx_sync2;
    uart_rx_reg2 <= uart_rx_reg1;
end

assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;
相关推荐
nuoxin1143 小时前
WILX1200HC-5TG144I替代 LCMXO2-1200HC-5TG144I(富利威)
人工智能·嵌入式硬件·fpga开发·电脑·硬件工程·dsp开发
Bahair_5 小时前
【FPGA】使用fdatool设计滤波器系数,使用FIR Compiler导入系数联合滤波
fpga开发
qq_411262428 小时前
硬件是ESP32-P4连接LAN8720A,正常初始化之后,设备DHCP失败
stm32·单片机·fpga开发
第二层皮-合肥13 小时前
【数据采集专栏】时钟同步(有时钟卡方案)
fpga开发
XINVRY-FPGA15 小时前
XCKU035-2FBVA676I AMD Xilinx Kintex UltraScale FPGA
arm开发·嵌入式硬件·网络安全·fpga开发·硬件工程·信号处理·fpga
米琪脆脆屋16 小时前
0-1学习FPGA之底层资源——LUT
fpga开发·fpga
TTGGGFF17 小时前
ModelSim SE 10.1c 超详细安装与激活保姆级教程(图文详解 2026 仅供学习)
学习·fpga开发
Aaron15881 天前
无人机反制中AOA+TDOA联合定位技术与雷达探测定位技术的应用对比分析
arm开发·嵌入式硬件·fpga开发·硬件工程·无人机·信息与通信·信号处理
暴风雨中的白杨1 天前
fpga复位电平与资源消耗对比测试
fpga开发
ALINX技术博客2 天前
【黑金云课堂】FPGA技术教程Linux开发:NVMe/Qt/OpenCV人脸检测
linux·qt·fpga开发