当前位置:   article > 正文

FPGA程序设计

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

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

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

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

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

  • 每个模块要用一个tb测试

  • testbench例子

  1. always #10 Clk = ~Clk;
  2. initial begin
  3. Clk = 1;
  4. Rst_n = 0;
  5. Cmd = 6'b000000;
  6. Go = 0;
  7. Tx_DATA = 8'd0;
  8. #2001;
  9. Rst_n = 1;
  10. #2000;
  11. //写数据操作,往EEPROM器件的B1地址写数据DA
  12. //第一次:起始位+EEPROM器件地址(7位)+写方向(1位)
  13. Cmd = STA | WR;
  14. Go = 1;
  15. Tx_DATA = 8'hA0 | 8'd0;//写方向
  16. @ (posedge Clk);
  17. #1;
  18. Go = 0;
  19. @ (posedge Trans_Done);
  20. #200;
  21. //第二次:写8位EEPROM的寄存器地址
  22. Cmd = WR;
  23. Go = 1;
  24. Tx_DATA = 8'hB1;//写地址B1
  25. @ (posedge Clk);
  26. #1;
  27. Go = 0;
  28. @ (posedge Trans_Done);
  29. #200;
  30. //第三次:写8位数据 + 停止位
  31. Cmd = WR | STO;
  32. Go = 1;
  33. Tx_DATA = 8'hda;//写数据DA
  34. @ (posedge Clk);
  35. #1;
  36. Go = 0;
  37. @ (posedge Trans_Done);
  38. #200;
  39. #5000000; //仿真模型的两次操作时间间隔
  40. //读数据操作,从EEPROM器件的B1地址读数据
  41. //第一次:起始位+EEPROM器件地址(7位)+写方向(1位)
  42. Cmd = STA | WR;
  43. Go = 1;
  44. Tx_DATA = 8'hA0 | 8'd0;//写方向
  45. @ (posedge Clk);
  46. #1;
  47. Go = 0;
  48. @ (posedge Trans_Done);
  49. #200;
  50. //第二次:写8位EEPROM的寄存器地址
  51. Cmd = WR;
  52. Go = 1;
  53. Tx_DATA = 8'hB1;//写地址B1
  54. @ (posedge Clk);
  55. #1;
  56. Go = 0;
  57. @ (posedge Trans_Done);
  58. #200;
  59. //第三次:起始位+EEPROM器件地址(7位)+读方向(1位)
  60. Cmd = STA | WR;
  61. Go = 1;
  62. Tx_DATA = 8'hA0 | 8'd1;//读方向
  63. @ (posedge Clk);
  64. #1;
  65. Go = 0;
  66. @ (posedge Trans_Done);
  67. #200;
  68. //第四次:读8位数据 + 停止位
  69. Cmd = RD | STO;
  70. Go = 1;
  71. @ (posedge Clk);
  72. #1;
  73. Go = 0;
  74. @ (posedge Trans_Done);
  75. #200;
  76. #2000;
  77. $stop;
  78. end
  • 每个模块要建立一个testbench

  • 时钟分频

  1. //系统时钟采用50MHz
  2. parameter SYS_CLOCK = 50_000_000;
  3. //SCL总线时钟采用400kHz
  4. parameter SCL_CLOCK = 400_000;
  5. //产生时钟SCL计数器最大值
  6. localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK/4 - 1;
  7. reg [19:0]div_cnt;
  8. reg en_div_cnt;
  9. always@(posedge Clk or negedge Rst_n)
  10. if(!Rst_n)
  11. div_cnt <= 20'd0;
  12. else if(en_div_cnt)begin
  13. if(div_cnt < SCL_CNT_M)
  14. div_cnt <= div_cnt + 1'b1;
  15. else
  16. div_cnt <= 0;
  17. end
  18. else
  19. div_cnt <= 0;
  20. wire sclk_plus = div_cnt == SCL_CNT_M;
  • 串转并

  1. reg[7:0]Rx_DATA;
  2. Rx_DATA <= {Rx_DATA[6:0],i2c_sdat};
  • 命令可以选择使用独热码编码

  1. localparam
  2. IDLE = 7'b0000001, //空闲状态
  3. GEN_STA = 7'b0000010, //产生起始信号
  4. WR_DATA = 7'b0000100, //写数据状态
  5. RD_DATA = 7'b0001000, //读数据状态
  6. CHECK_ACK = 7'b0010000, //检测应答状态
  7. GEN_ACK = 7'b0100000, //产生应答状态
  8. GEN_STO = 7'b1000000; //产生停止信号
  • 亚稳态

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

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

异步信号采集

  1. always@(posedge clk or posedge reset)
  2. if(reset)begin
  3. uart_rx_sync1 <= 1'b0;
  4. uart_rx_sync2 <= 1'b0;
  5. end
  6. else begin
  7. uart_rx_sync1 <= uart_rx;
  8. uart_rx_sync2 <= uart_rx_sync1;
  9. end

边沿检测

  1. //下降沿检测
  2. always@(posedge clk or posedge reset)
  3. if(reset)begin
  4. uart_rx_reg1 <= 1'b0;
  5. uart_rx_reg2 <= 1'b0;
  6. end
  7. else begin
  8. uart_rx_reg1 <= uart_rx_sync2;
  9. uart_rx_reg2 <= uart_rx_reg1;
  10. end
  11. assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/808141
推荐阅读
相关标签
  

闽ICP备14008679号