• I2C控制器的Verilog建模之二


    前言:接着上一篇的I2C写操作,今天要实现一个I2C的读操作。虽然在ADV7181B配置内部寄存器时没有必要使用到读操作,但是为了进一步确认寄存器是否在I2C写模块下被正确配置,这一步是必不可少的。

    设计思路:由于最终的应用里I2C读模块在调试结束后还是要被剔除,因此决定还是另外建一个读的状态机独立于之前的写状态机。读状态机的思路基本和写状态机的思路一样,需要注意的是一次写操作需要两次的START信号和最后一字节传输结束后的NON-ACKNOWLEDGE。

    改进和注意点:相比之前的写模块,读模块完善了以下这些

          (a)时钟信号在一系列写操作完毕之后拉高,不再跳变;

          (b)添加了使能信号,便于安排模块在写模块完成一些了写操作后才开始被激励工作;

          (c)三态口:修改了SDAT_R,使之在LINK为0,即三态口读方向时候SDAR_T保持1。以及LINK信号在空闲状态下释放总线;

    未解决的问题:因为读写两个独立的模块各自用到一个三态口,即各自有一对I2C_SCLK和I2C_SDAT,因此在顶层就驱动同一个芯片的引脚之前,还需要一个复用的逻辑开关分时切换这两个驱动源。因为之前没有这样用多个三态口驱动一个同一个三态口,综合是否可行将在下一篇I2C测试里给出答案。p.s.当然如果整合两个状态机到同一个模块下就不存在这样的问题。

    源码:

      1 `timescale 1 ns / 1 ps
      2 `define SIM
      3 `define SYS_CLK    50000000
      4 `define I2C_CLK    100000
      5 `define I2C_DIV    `SYS_CLK/`I2C_CLK
      6 `define ADV7180
      7 `define SCLK_CNT_WIDTH    9
      8 //version:v1.0
      9 //mend bug:            WHEN IN IDLE SET SCLK HIGH; 
     10 module i2c_read_controller(
     11                                 sys_clk,
     12                                 sys_rst_n,
     13                                 sys_rreq_i,
     14                                 sys_rd_en,
     15                                 rd_reg_addr_i,
     16                                 sys_data_o,
     17                                 i2c_rd_idle_o,
     18                                 i2c_rd_ack_o,
     19                                 i2c_sclk,
     20                                 i2c_sdat
     21                                 );
     22                                 
     23 input sys_clk;
     24 input sys_rst_n;
     25 input sys_rreq_i;
     26 input sys_rd_en;                //由其他信号激励使能
     27 input [7:0] rd_reg_addr_i;    //从机寄存器地址
     28 output [7:0] sys_data_o;    //待写的数据
     29 output i2c_rd_idle_o;        //模块空闲
     30 output i2c_rd_ack_o;    //非I2C的ACK,模块的ack
     31 output i2c_sclk;
     32 inout i2c_sdat;
     33 `ifdef ADV7180
     34     parameter DEVICE_READ = 8'h40;        //器件读操作地址
     35     parameter DEVICE_WRITE = 8'h41;        //器件写操作地址
     36 `endif
     37 
     38 `ifdef SIM
     39         parameter ST_WIDTH = 56;
     40         parameter IDLE = "IDLE...",
     41                         START1 = "START1.",
     42                         WR_SLAVE = "WR_SLAV",
     43                         ACK1 = "ACK1...",
     44                         SET_REG = "SET_REG",
     45                         ACK2 = "ACK2...",
     46                         START2 = "START2",
     47                         RD_SLAVE = "RD_SLAV",
     48                         ACK3 = "ACK3...",
     49                         DATA = "DATA...",
     50                         NACK4 = "NACK4..",
     51                         STOP = "STOP...";
     52 
     53 `else
     54         `define FSM    12
     55         parameter ST_WIDTH = 12;
     56         parameter IDLE = `FSM'b0000_0000_0001,
     57                         START1 =  `FSM'b0000_0000_0010,    //写操作一共有1个start,读操作一共2个start
     58                         WR_SLAVE =  `FSM'b0000_0000_0100,
     59                         ACK1 =  `FSM'b0000_0000_1000,
     60                         SET_REG =  `FSM'b0000_0001_0000,
     61                         ACK2 =  `FSM'b0000_0010_0000,
     62                         START2 = `FSM'b0000_0100_0000,
     63                         RD_SLAVE =  `FSM'b0000_1000_0000,
     64                         ACK3 =  `FSM'b0001_0000_0000,
     65                         DATA = `FSM'b0010_0000_0000,
     66                         NACK4 = `FSM'b0100_0000_0000,
     67                         STOP =  `FSM'b1000_0000_0000;
     68 `endif
     69 //caputre the posedge of sys_rreq_i;
     70 reg sys_rreq_r0 = 0;
     71 always @ (posedge sys_clk) begin
     72 if(sys_rst_n == 1'b0)    sys_rreq_r0 <= 0;
     73 else sys_rreq_r0 <= sys_rreq_i;
     74 end
     75 wire do_rreq = sys_rreq_i & ~sys_rreq_r0 & sys_rd_en;
     76 //generate the rd_start;
     77 reg rd_start = 0;
     78 always @ (posedge sys_clk) begin
     79 if(sys_rst_n == 1'b0)    rd_start <= 0;
     80 else if(i2c_rd_ack_o == 1'b1) rd_start <= 0;
     81 else if(do_rreq)    rd_start <= 1;
     82 else rd_start <= rd_start;
     83 end
     84 //GENERATE SCLK_R
     85 reg [`SCLK_CNT_WIDTH-1:0] sclk_cnt = 0;
     86 always @ (posedge sys_clk) begin
     87 if(1'b0 == sys_rst_n) sclk_cnt <= 0;
     88 else if((sclk_cnt < `I2C_DIV-1)&&(sys_rd_en == 1'b1)&&(rd_start == 1'b1)) sclk_cnt <= sclk_cnt + 1'd1;
     89 else sclk_cnt <= 0;
     90 end
     91 
     92 `define SCLK_POS    (sclk_cnt == `SCLK_CNT_WIDTH'd499)
     93 `define SCLK_HIGH    (sclk_cnt == `SCLK_CNT_WIDTH'd124)
     94 `define SCLK_NEG    (sclk_cnt == `SCLK_CNT_WIDTH'd249)
     95 `define SCLK_LOW    (sclk_cnt == `SCLK_CNT_WIDTH'd374)
     96 wire i2c_sclk_w;
     97 assign i2c_sclk_w = ((sys_rd_en == 1'b1)&&(sclk_cnt <= `SCLK_CNT_WIDTH'd249))?1'b1:1'b0;
     98 
     99 
    100 //FSM
    101 reg [7:0] data2host = 0;
    102 reg [7:0] sys_data_o = 0;
    103 reg sdat_r = 1;
    104 reg link = 0;        //控制三态口读写方向,默认为读方向0,写时为1
    105 reg [3:0] bit_cnt = 4'd0;
    106 reg [ST_WIDTH-1:0] c_st = IDLE;
    107 reg [ST_WIDTH-1:0] n_st = IDLE;
    108 //FSM-1
    109 always @ (posedge sys_clk) begin
    110 if(1'b0 == sys_rst_n) c_st <= IDLE;
    111 else c_st <= n_st;
    112 end
    113 //fsm-2
    114 //实际的状态转移中ack[2:0]比物理等待的ack少四分之一
    115 always @ (*) begin
    116     n_st = IDLE;
    117     case(c_st)
    118     IDLE:begin
    119                 n_st = (rd_start == 1'b1)?START1:IDLE;end
    120     START1:begin
    121                     n_st = (`SCLK_LOW)?WR_SLAVE:START1;end    //sclk为高电平中心时转移
    122     WR_SLAVE:begin
    123                             n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK1:WR_SLAVE;end//数据在低电平是更新
    124     ACK1:begin 
    125                 n_st = (`SCLK_NEG)?SET_REG:ACK1;end//为保证下一步设置寄存器,提前1/4进入下一个状态
    126     SET_REG:begin
    127                         n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK2:SET_REG;end//数据在低电平是更新
    128     ACK2:begin
    129                     n_st = (`SCLK_NEG)?START2:ACK2;end
    130     START2:begin
    131                     n_st = (`SCLK_NEG)?RD_SLAVE:START2;end
    132     RD_SLAVE:begin
    133                             n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK3:RD_SLAVE;end
    134 //为保证下一步设置寄存器,提前1/4进入下一个状态
    135     ACK3:begin
    136                     n_st = (`SCLK_NEG)?DATA:ACK3;end
    137     DATA:begin
    138                         n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?NACK4:DATA;end
    139     NACK4:begin
    140                     n_st = (`SCLK_NEG)?STOP:NACK4;end
    141     STOP:begin
    142                     n_st = (`SCLK_NEG)?IDLE:STOP;end
    143     default:begin
    144                     n_st = IDLE;end
    145     endcase
    146 end
    147 //FSM-3
    148 always @ (posedge sys_clk) begin
    149 if(sys_rst_n == 1'b0) begin
    150                                 link <= 1'd0;    //释放总线
    151                                 data2host <= 8'd0;
    152                                 bit_cnt <= 4'd0;
    153                                 sdat_r <= 1'd1;
    154                                 sys_data_o <= 0;
    155                                 end
    156 else begin
    157     case(c_st)
    158     IDLE:begin
    159                                 link <= 1'd0;
    160                                 data2host <= DEVICE_WRITE;
    161                                 bit_cnt <= 4'd0;
    162                                 sdat_r <= 1'd1;
    163                                 sys_data_o <= sys_data_o;
    164                                 end
    165     START1:begin
    166                                 link <= 1'd1;
    167                                 sys_data_o <= sys_data_o;
    168                                 bit_cnt <= 4'd1;
    169                                 data2host <= (`SCLK_LOW)?data2host<<1:data2host;
    170                                 if(`SCLK_HIGH) begin
    171                                                         sdat_r <= 1'b0;end
    172                                 else if(`SCLK_LOW) begin
    173                                                         sdat_r <= data2host[7];end    //pull down,由于data2host缓存一级的缘故,需要提前在START里输出第MSB位
    174                                 else begin
    175                                                 sdat_r <= sdat_r;end
    176                 end
    177     WR_SLAVE:begin
    178                             sys_data_o <= sys_data_o;
    179                             if(`SCLK_LOW) begin
    180                                     link <= (bit_cnt == 4'd8)?1'b0:1'b1;    //释放数据总线
    181                                     bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1;
    182                                     data2host <= {data2host[6:0],1'd0};//左移一位
    183                                     sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end
    184                             else begin
    185                                     link <= link;
    186                                     bit_cnt <= bit_cnt;
    187                                     data2host <= data2host;
    188                                     sdat_r <= sdat_r;end
    189                         end
    190     ACK1:begin
    191                                 link <= 1'd0;
    192                                 sys_data_o <= sys_data_o;
    193                                 data2host <= (`SCLK_POS)?rd_reg_addr_i:data2host;    //读入待写的寄存器地址
    194                                 bit_cnt <= 4'd0;
    195                                 sdat_r <= 1'd1;
    196                                 end
    197     SET_REG:begin
    198                             sys_data_o <= sys_data_o;
    199                             if(`SCLK_LOW) begin
    200                                     link <= (bit_cnt == 4'd8)?1'b0:1'b1;    //释放数据总线
    201                                     bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1;
    202                                     data2host <= {data2host[6:0],1'd0};//左移一位
    203                                     sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end
    204                             else begin
    205                                     link <= link;
    206                                     bit_cnt <= bit_cnt;
    207                                     data2host <= data2host;
    208                                     sdat_r <= sdat_r;end
    209                         end
    210     ACK2:begin
    211                                 link <= 1'd0;
    212                                 sys_data_o <= sys_data_o;
    213                                 data2host <= (`SCLK_POS)?DEVICE_READ:data2host;    //读入待写的寄存器地址
    214                                 bit_cnt <= 4'd0;
    215                                 sdat_r <= 1'd1;
    216                                 end
    217     START2:begin
    218                     link <= (`SCLK_LOW)?1'b1:link;
    219                     sys_data_o <= sys_data_o;
    220                     data2host <= data2host;
    221                     bit_cnt <= bit_cnt;
    222                     sdat_r <= (`SCLK_HIGH)?1'b0:sdat_r;
    223                     end
    224     RD_SLAVE:begin
    225                             sys_data_o <= sys_data_o;
    226                             if(`SCLK_LOW) begin
    227                                     link <= (bit_cnt == 4'd8)?1'b0:1'b1;
    228                                     bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 
    229                                     data2host <= {data2host[6:0],1'd0};//左移一位
    230                                     sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end
    231                             else begin
    232                                     link <= link;
    233                                     bit_cnt <= bit_cnt;
    234                                     data2host <= data2host;
    235                                     sdat_r <= sdat_r;end
    236                             end
    237     ACK3:begin
    238                 link <= 1'b0;
    239                 bit_cnt <= 4'd0;
    240                 sys_data_o <= sys_data_o;
    241                 data2host <= 0;
    242                 sdat_r <= 1'd1;end
    243     DATA:begin
    244                         sys_data_o <= sys_data_o;
    245                             if(`SCLK_HIGH) begin
    246                                     link <= (bit_cnt == 4'd8)?1'b1:1'b0;    //为主设备产生NACK准备
    247                                     bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1;
    248                                     data2host[7:1] <= data2host[6:0];//左移一位
    249                                     data2host[0] <= sdat_r;end
    250                             else begin
    251                                     link <= link;
    252                                     bit_cnt <= bit_cnt;
    253                                     data2host <= data2host;
    254                                     sdat_r <= sdat_r;end
    255                         end
    256     NACK4:begin
    257                     link <= 1'd1;
    258                     sdat_r <= 1'd1;//预先拉低
    259                     bit_cnt <= bit_cnt;
    260                     sys_data_o <= data2host;end
    261     STOP:begin
    262                     link <= 1'b1;
    263                     bit_cnt <= bit_cnt;
    264                     sys_data_o <= sys_data_o;
    265                     data2host <= data2host;
    266                     if(`SCLK_LOW) begin
    267                                             sdat_r <= 1'b0;end
    268                     else if(`SCLK_HIGH) begin
    269                                             sdat_r <= 1'b1;end
    270                     else begin
    271                                             sdat_r <= sdat_r;end
    272             end
    273     default:begin
    274                                 link <= 1'd0;
    275                                 data2host <= 8'd0;
    276                                 sys_data_o <= sys_data_o;
    277                                 bit_cnt <= 4'd0;
    278                                 sdat_r <= 1'd1;
    279                                 end
    280     endcase
    281     end
    282 end
    283 //assign
    284 assign i2c_sdat = (link == 1'b1)?sdat_r:8'hzz;
    285 assign i2c_rd_idle_o = (c_st == IDLE)?1'b1:1'b0;
    286 assign i2c_rd_ack_o = ((c_st == STOP)&&(`SCLK_NEG))?1'b1:1'b0;
    287 assign i2c_sclk = (c_st != IDLE)?i2c_sclk_w:1'b1;
    288 
    289 
    290 endmodule

    控制读模块源码2:

     1 `timescale 1 ns / 1 ps
     2 `define LUT_WIDTH 4
     3 module adv7181_read_back(
     4                                             sys_clk,
     5                                             sys_rst_n,
     6                                             i2c_rd_ack_i,
     7                                             rd_back_done_o,
     8                                             sys_rreq_o,
     9                                             rd_reg_addr_o
    10                                             );
    11 input sys_clk;
    12 input sys_rst_n;
    13 input i2c_rd_ack_i;
    14 output rd_back_done_o;
    15 output sys_rreq_o;
    16 output [7:0] rd_reg_addr_o;
    17 
    18 //generate rreq_o
    19 reg sys_rreq_o = 0;
    20 reg [`LUT_WIDTH-1:0] lut_index = 0;
    21 reg [7:0] lut_data = 0;
    22 always @ (posedge sys_clk) begin
    23 if(1'b0 == sys_rst_n) begin
    24                                 sys_rreq_o <= 1;
    25                                 lut_index <= 0;end
    26 else if((i2c_rd_ack_i == 1'b1)&&(rd_back_done_o == 1'b0)) begin
    27                                 sys_rreq_o <= 1;
    28                                 lut_index <= lut_index + 1'd1;end
    29 else begin
    30         sys_rreq_o <= 0;
    31         lut_index <= lut_index;end
    32 end
    33 //assign
    34 assign rd_back_done_o = (lut_index == `LUT_WIDTH'd15)?1'b1:1'b0;
    35 assign rd_reg_addr_o = lut_data;
    36 //lut 
    37 always @ (*) begin
    38     case(lut_index)
    39     `LUT_WIDTH'd0:lut_data <= 8'h23;
    40     `LUT_WIDTH'd1:lut_data <= 8'h41;
    41     `LUT_WIDTH'd2:lut_data <= 8'hf2;
    42     `LUT_WIDTH'd3:lut_data <= 8'ha3;
    43     `LUT_WIDTH'd4:lut_data <= 8'h43;
    44     `LUT_WIDTH'd5:lut_data <= 8'h13;
    45     `LUT_WIDTH'd6:lut_data <= 8'h65;
    46     `LUT_WIDTH'd7:lut_data <= 8'h76;
    47     `LUT_WIDTH'd8:lut_data <= 8'h85;
    48     `LUT_WIDTH'd9:lut_data <= 8'h93;
    49     `LUT_WIDTH'd10:lut_data <= 8'h14;
    50     `LUT_WIDTH'd11:lut_data <= 8'h13;
    51     `LUT_WIDTH'd12:lut_data <= 8'h15;
    52     `LUT_WIDTH'd13:lut_data <= 8'h11;
    53     `LUT_WIDTH'd14:lut_data <= 8'h11;
    54     `LUT_WIDTH'd15:lut_data <= 8'h19;
    55     endcase
    56 end
    57 
    58 endmodule

    仿真源码3:

     1 `timescale 1 ns / 1 ps
     2 module tb_read();
     3 reg sys_clk;
     4 reg sys_rst_n;
     5 
     6 initial begin
     7 sys_clk=1;
     8 sys_rst_n=0;
     9 #100 sys_rst_n=1;
    10 end
    11 
    12 always begin
    13 #10 sys_clk=~sys_clk;end
    14 
    15 wire i2c_rd_ack;
    16 wire sys_rreq;
    17 wire [7:0] rd_reg_addr;
    18 
    19 wire i2c_sclk;
    20 wire i2c_sdat;
    21 wire i2c_rd_idle;
    22 wire [7:0] sys_data_out;
    23  i2c_read_controller    u0(
    24                                 .sys_clk( sys_clk ),
    25                                 .sys_rst_n( sys_rst_n ),
    26                                 .sys_rreq_i( sys_rreq ),
    27                                 .sys_rd_en( 1'b1 ),
    28                                 .rd_reg_addr_i( rd_reg_addr ),
    29                                 .sys_data_o( sys_data_out ),
    30                                 .i2c_rd_idle_o( i2c_rd_idle ),
    31                                 .i2c_rd_ack_o( i2c_rd_ack ),
    32                                 .i2c_sclk( i2c_sclk ),
    33                                 .i2c_sdat( i2c_sdat )
    34                                 );
    35                                 
    36 wire rd_back_done;
    37 
    38 adv7181_read_back                u1(
    39                                             .sys_clk( sys_clk ),
    40                                             .sys_rst_n( sys_rst_n ),
    41                                             .i2c_rd_ack_i( i2c_rd_ack ),
    42                                             .rd_back_done_o( rd_back_done ),
    43                                             .sys_rreq_o( sys_rreq ),
    44                                             .rd_reg_addr_o( rd_reg_addr )
    45                                             );
    46 endmodule
  • 相关阅读:
    蓝桥杯训练 2n皇后问题
    AtCoder Grand Contest #026 B
    AtCoder Grand Contest #026 A
    AtCoder Beginner Contest 103
    7-26 单词长度(15 分)
    uva 10006 Carmichael Numbers
    java并发带返回结果的批量任务执行(CompletionService:Executor + BlockingQueue)
    并发容器之CopyOnWriteArrayList
    模板模式
    静态工具类中使用注解注入service
  • 原文地址:https://www.cnblogs.com/loadomain/p/3250986.html
Copyright © 2020-2023  润新知