赞
踩
模块框图如下所示
输入输出信号:
整体的思路如下:
通过输入的命令组合,完成一次8字节数据的传输。定义了6种命令,
WR | 写数据请求 (6’b000_001) |
---|---|
STA | 起始位请求(6’b000_010) |
RD | 读数据请求(6’b000_100) |
STO | 停止位请求(6’b001_000) |
ACK | 应答位请求(6’b010_000) |
NACK | 无应答请求(6‘b100_000) |
采用了状态机里面套用序列机的思路,在每个状态里面分为四步完成一位数据的传输,使用计数器来控制。定义的状态如下(独热码编码):
state | discription |
---|---|
IDLE | 空闲状态 |
GEN_STA | 产生起始信号 |
WR_DATA | 写数据状态 |
RD_DATA | 读数据状态 |
CHECK_ACK | 检测应答信号 |
GEN_ACK | 产生应答信号 |
GNE_STO | 产生停止信号 |
状态机转换如下所示:
代码如下所示,有很清晰的注释:
module i2c_bit_shift ( input wire clk , input wire rst_n , input wire [5:0] cmd , //控制总线实现各种传输的各种命令组合 input wire go , //整个模块的启动信号 input wire [7:0] Tx_Data , //总线要发送的8位数据 output reg i2c_sclk , //i2c时钟总线 inout wire i2c_sdat , //i2c数据总线 output reg Trans_Done , //传输完成信号 output reg [7:0] Rx_Data , //总线要接收的8位数据 output reg ack_o //从机时候应答标志 ); localparam IDLE = 7'b0000_001, //空闲状态 GEN_STA = 7'b0000_010, //产生起始信号 WR_DATA = 7'b0000_100, //写数据状态 RD_DATA = 7'b0001_000, //读数据状态 CHECK_ACK = 7'b0010_000, //检测应答信号 GEN_ACK = 7'b0100_000, //产生应答信号 GNE_STO = 7'b1000_000; //产生停止信号 localparam WR = 6'b000_001, //写数据请求 STA = 6'b000_010, //起始位请求 RD = 6'b000_100, //读数据请求 STO = 6'b001_000, //停止位请求 ACK = 6'b010_000, //应答位请求 NACK = 6'b100_000; //无应答请求 parameter SYS_CLOCK = 50_000_000; //系统采用50MHz时钟 parameter SCL_CLOCK = 400_000 ; //SCL总线时钟采用400khz localparam SCL_CNT_M = SYS_CLOCK / SCL_CLOCK / 4 - 1; //产生时钟SCL计数器最大值 reg en_div_cnt ; //计数器使能信号 reg i2c_sda_oe ; //三态门使能信号,1:输出,0:输入 reg [6:0] state ; //状态机的状态变量 reg [5:0] cnt ; //计数器 reg i2c_sdat_o ; //输出的i2c_sdat_o reg [19:0] div_cnt ; //分频信号 wire sclk_plus = (div_cnt == SCL_CNT_M); //dic_cnt计数到最大值产生一个高脉冲 //reset为低时,若干信号的初始化 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin en_div_cnt <= 1'b0; i2c_sda_oe <= 1'b1; state <= IDLE; cnt <= 0; i2c_sdat_o <= 1'b0; i2c_sclk <= 1'b1; Rx_Data <= 8'd0; end end //一段式状态机 always @(posedge clk or negedge rst_n) begin case(state) IDLE: begin Trans_Done <= 1'b0; i2c_sda_oe <= 1'b1; if(go) begin //整个模块的启动信号go en_div_cnt <= 1'b1; //使能div_cnt计数器,跳转的状态都需要这个计数器 if(cmd & STA ) //起始命令 state <= GEN_STA; //跳转到起始状态 else if(cmd & WR) //写命令 state <= WR_DATA; //跳转到写数据状态 else if(cmd & RD) //读命令 state <= RD_DATA; //跳转到读数据状态 else state <= IDLE; //都不满足则跳转到IDLE状态 end else begin en_div_cnt <= 1'b0; //模块未启动 state <= IDLE; //跳转到IDLE状态,且不使能div_cnt计数器 end end GEN_STA:begin //i2c_sclk为高电平时,i2c_sda从高电平变成低电平产生起始位 if(sclk_plus) begin //只有在sclk_plus产生高脉冲的时候,div_cnt计数到最大值 if(cnt == 3) //cnt计数到3,归0 cnt <= 0; else cnt <= cnt + 1'b1; //否则计数器自增1 case(cnt) //分四步 0:begin i2c_sdat_o <= 1'b1; i2c_sda_oe <= 1'b1; end //cnt为0时,i2c_sdat_o拉高,三态门输出 1:begin i2c_sclk <= 1'b1; end //cnt为1时,拉高i2c_sclk 2:begin i2c_sdat_o <= 0; i2c_sclk <= 1; end //cnt为2时,继续拉高i2c_sclk,拉低i2c_sdat_o 3:begin i2c_sclk <= 0; end //cnt为3时,拉低i2c_sclk default: begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end endcase if(cnt == 3) begin if(cmd & WR) state <= WR_DATA; //跳转到写数据状态 else if(cmd & RD) state <= RD_DATA; //跳转到读数据状态 end end end WR_DATA:begin //写数据时,只有在sclk为低电平的时候,sda才可以变化 if(sclk_plus) begin //8位数据,每位数据分4步,故cnt计数器计数到31 if(cnt == 31) cnt <= 0; else cnt <= cnt + 1'b1; case(cnt) 0,4,8,12,16,20,24,28: begin i2c_sdat_o <= Tx_Data[7-cnt[4:2]]; //在GNE_STA状态时,i2c_sclk拉低了,所以此时把Tx_data一位传给i2s_sdat_o i2c_sda_oe <= 1'b1; //三态门输出使能 end 1,5,9,13,17,21,25,29: begin i2c_sclk <= 1'b1; //sclk posedge 拉高i2c_sclk end 2,6,10,14,18,22,26,30: begin i2c_sclk <= 1'b1; //sclk keep high 继续拉高i2c_sclk end 3,7,11,15,19,23,27,31: begin i2c_sclk <= 1'b0; //sclk negedge 拉低i2c_sclk,以便在下次传输Tx_Data的一位 end default: begin i2c_sdat_o <= 1; i2c_sclk <= 1; end endcase if(cnt == 31) begin state <= CHECK_ACK; //Tx_Data的8位写完之后,跳转到检查应答位状态 end end end CHECK_ACK:begin if(sclk_plus) begin if(cnt == 3) cnt <= 0; else cnt <= cnt + 1; case(cnt) //应答位的检查依然分四步完成,使用计数器cnt来控制 0: begin i2c_sda_oe <= 1'b0; i2c_sclk <= 1'b0; end //拉低i2c_sclk信号,并且设置三态门为输入 1: begin i2c_sclk <= 1; end //拉高i2c_sclk 2: begin ack_o <= i2c_sdat; i2c_sclk <= 1; end //拉高i2c_sclk,并且把i2c_sdat赋值给ack_o, 3: begin i2c_sclk <= 0; end //拉低i2c_sclk default: begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end //上面连续两次拉高i2c_sclk是为了占空比为50%的i2c_sclk时钟 endcase if(cnt == 3) begin if(cmd & STO) state <= GNE_STO; //跳转到产生停止位状态 else begin state <= IDLE; Trans_Done <= 1'b1; //一次传输完成 end end end end GNE_STO : begin //在i2c_sclk为高电平,i2c_sda从低到高则为停止信号 if(sclk_plus) begin //与产生起始信号类似 if(cnt == 3) cnt <= 0; else cnt <= cnt + 1'b1; case(cnt) 0: begin i2c_sdat_o <= 1'b0; i2c_sda_oe <= 1'b1; end //三态门输出, i2c_sda拉低 1: begin i2c_sclk <= 1'b1; end //拉高i2c_sclk 2: begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end //拉高i2c_sda,i2c_sclk 3: begin i2c_sclk <= 1'b1; end default: begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end endcase if(cnt == 3) begin Trans_Done <= 1'b1; //传输完成 state <= IDLE; end end end RD_DATA: begin //取数据 if(sclk_plus) begin if(cnt == 31) cnt <= 0; else cnt <= cnt + 1'b1; //8位,每位分4步完成 case(cnt) 0,4,8,12,16,20,24,28: begin i2c_sda_oe <= 1'b0; i2c_sclk <= 0; //set data,拉低i2c_sclk,三态门设置为输入 end 1,5,9,13,17,21,25,29: begin i2c_sclk <= 1'b1; //sclk posedge, 拉高i2c_scclk end 2,6,10,14,18,22,26,30: begin i2c_sclk <= 1'b1; //sclk keep high 利用移位的方法来存储,技巧需掌握 Rx_Data <= {Rx_Data[6:0], i2c_sdat}; end 3,7,11,15,19,23,27,31: //sclk negedge begin i2c_sclk <= 1'b0; end default: begin i2c_sdat_o <= 1; i2c_sclk <= 1; end endcase if(cnt == 31) begin state <= GEN_ACK; end end end GEN_ACK: begin //产生应答信号 if(sclk_plus) begin //分四步完成 if(cnt == 3) cnt <= 0; else cnt <= cnt + 1'b1; case(cnt) 0: begin i2c_sda_oe <= 1'b1; //三态门输出 i2c_sclk <= 1'b0; //拉低i2c_sclk if(cmd & ACK) //产生ACK,输出0 i2c_sdat_o <= 1'b0; else if(cmd & NACK) //产生NOACK,输出1 i2c_sdat_o <= 1'b1; end 1: begin i2c_sclk <= 1'b1; end 2: begin i2c_sclk <= 1'b1; end 3: begin i2c_sclk <= 1'b0; end default: begin i2c_sdat_o <= 1; i2c_sclk <= 1; end endcase if(cnt == 3) begin if(cmd & STO) state <= GNE_STO; //跳转到产生停止位状态 else begin state <= IDLE; Trans_Done <= 1'b1; //传输完成 end end end end default: begin i2c_sclk <= 1'b1; Trans_Done <= 1'b0; i2c_sdat_o <= 1'b0; i2c_sda_oe <= 1'b1; cnt <= 0; end endcase end //在en_div_cnt使能,div_cnt开始奇数,计数大最大值循环计数 always @(posedge clk or negedge rst_n) begin 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 <= 20'd0; end assign i2c_sdat = !i2c_sdat_o && i2c_sda_oe ? 1'b0 : 1'bz; //三态门设置 endmodule
测试的tb文件如下所示,分别向0x0A,0x0B,0x0C,0x0D,0X0F,写入1,2,3,4,5。然后再把它们读出来。
`timescale 1ns/1ns module i2c_bit_shift_tb; reg Clk; reg Rst_n; reg [5:0]Cmd; reg Go; wire [7:0]Rx_DATA; reg [7:0]Tx_DATA; wire Trans_Done; wire ack_o; wire i2c_sclk; wire i2c_sdat; pullup PUP (i2c_sdat); localparam WR = 6'b000001, // 写请求 STA = 6'b000010, //起始位请求 RD = 6'b000100, //读请求 STO = 6'b001000, //停止位请求 ACK = 6'b010000, //应答位请求 NACK = 6'b100000; //无应答请求 /* i2c_bit_shift i2c_bit_shift( .Clk(Clk), .Rst_n(Rst_n), .Cmd(Cmd), .Go(Go), .Rx_DATA(Rx_DATA), .Tx_DATA(Tx_DATA), .Trans_Done(Trans_Done), .ack_o(ack_o), .i2c_sclk(i2c_sclk), .i2c_sdat(i2c_sdat) ); */ i2c_bit_shift i2c_bit_shift( .clk(Clk), .rst_n(Rst_n), .cmd(Cmd), .go(Go), .Rx_Data(Rx_DATA), .Tx_Data(Tx_DATA), .Trans_Done(Trans_Done), .ack_o(ack_o), .i2c_sclk(i2c_sclk), .i2c_sdat(i2c_sdat) ); M24LC04B M24LC04B( .A0(0), .A1(0), .A2(0), .WP(0), .SDA(i2c_sdat), .SCL(i2c_sclk), .RESET(~Rst_n) ); initial Clk = 1; always #10 Clk = ~Clk; initial begin Rst_n = 0; Cmd = 0; Go = 0; Tx_DATA = 0; #2001; Rst_n = 1; #2000; write_one_byte(8'hA0,8'h0A,8'hd1); //#20000; write_one_byte(8'hA0,8'h0B,8'hd2); //#20000; write_one_byte(8'hA0,8'h0C,8'hd3); //#20000; write_one_byte(8'hA0,8'h0D,8'hd4); //#20000; write_one_byte(8'hA0,8'h0F,8'hd5); //#20000; read_one_byte(8'hA0,8'h0A); //#20000; read_one_byte(8'hA0,8'h0B); //#20000; read_one_byte(8'hA0,8'h0C); //#20000; read_one_byte(8'hA0,8'h0D); //#20000; read_one_byte(8'hA0,8'h0F); //#20000; $stop; end task write_one_byte; input [7:0]device_id; //器件地址 input [7:0]mem_address; //寄存器地址 input [7:0]data; //需要写的数据 begin Cmd = WR | STA; //写入产生起始位和写数据命令 Go = 1; //启动整个模块 Tx_DATA = device_id; //把器件地址赋值给Tx_Data #20; Go = 0; //延迟一个时钟周期拉低go信号 #200; @(posedge Trans_Done); //等待此次传输数据完成 #20; //#20000; Cmd = WR; //传入写数据命令 Go = 1; //启动整个模块 Tx_DATA = mem_address; //把寄存器地址赋值给Tx_Data #20; Go = 0; //延迟一个时钟周期拉低go信号 #200; @(posedge Trans_Done); //等待此次数据传输完成 //#20000; #20 Cmd = WR | STO; //传入写数据和产生停止位的命令 Go = 1; //启动整个模块 Tx_DATA = data; //把需要写入的数据赋值给Tx_Data #20; Go = 0; //延迟一个时钟周期拉低go信号 #200; @(posedge Trans_Done); //等待此次传输数据完成 #20000; end endtask task read_one_byte; input [7:0]device_id; //传入器件地址 input [7:0]mem_address; //传入寄存器地址 begin Cmd = WR | STA; //传入写数据和产生开始位的命令 Go = 1; //启动整个模块 Tx_DATA = device_id; //把器件地址赋值给Tx_Data #20; Go = 0; //延迟一个时钟周期拉低go信号 #200; @(posedge Trans_Done); //等待此次传输数据完成 #20000; Cmd = WR | STO; Go = 1; Tx_DATA = mem_address; #20; Go = 0; #200; @(posedge Trans_Done); //#20000; #20; Cmd = WR | STA; Go = 1; Tx_DATA = device_id | 8'd1; #20; Go = 0; #200; @(posedge Trans_Done); //#20000; #20; Cmd = RD | NACK | STO; Go = 1; #20; Go = 0; #200; @(posedge Trans_Done); //#20000; #20; end endtask endmodule
仿真波形如下所示,可知成功读出了数据:
由于i2c_bit_shift模块只可以发送一个字节的数据,而一次写输入或者时读输出需要写入好几个字节。因此可以为写输入和输入分别分配若干个写字节的任务,两者均是从IDLE状态开始。但是这些任务时是顺序的,必须等待前一个任务完成,此时我们的Trans_done信号就派上用场了。故可以使用状态机来设计。
对于一次写:
对于一次读:
有如下的状态(采取独热码编码):
IDLE | 空闲状态 |
---|---|
WR_REG | 写状态 |
WAIT_WR_DONE | 等待写完成状态 |
WR_REG_DONE | 写完成状态 |
RD_REG | 读状态 |
WAIT_RD_DONE | 等待读完成状态 |
RD_REG_DONE | 读完成状态 |
状态转换图:
控制模块的代码如下(代码中有很详细的注释),便不多解释
module i2c_ctrl( input wire clk , //时钟信号 input wire rst_n , //复位信号 input wire wrreg_req , //写请求信号 input wire rdreg_req , //读请求信号 input wire [15:0] addr , //16位地址输入 input wire addr_mode , //输入地址的模式,0:8位的地址 1:16位的地址 input wire [7:0] wrdata , //总线发送的8位数据 output reg [7:0] rddata , //总线接收的8位数据 input wire [7:0] device_id , //i2c器件的ID output reg RW_Done , //读/写完成的标志 output reg ack , //从机是否应答标志 output wire i2c_sclk , //i2c时钟总线 inout wire i2c_sdat ); localparam IDLE = 7'b0000_001, //空闲状态 WR_REG = 7'b0000_010, //写状态 WAIT_WR_DONE= 7'b0000_100, //等待写完成状态 WR_REG_DONE = 7'b0001_000, //写完成状态 RD_REG = 7'b0010_000, //读状态 WAIT_RD_DONE= 7'b0100_000, //等待读完成状态 RD_REG_DONE = 7'b1000_000; //读完成状态 localparam WR = 6'b000_001, //写数据请求 STA = 6'b000_010, //起始位请求 RD = 6'b000_100, //读数据请求 STO = 6'b001_000, //停止位请求 ACK = 6'b010_000, //应答位请求 NACK = 6'b100_000; //无应答请求 reg [5:0] cmd ; reg [7:0] Tx_Data ; reg go ; reg [6:0] state ; reg [7:0] cnt ; //reg [15:0] reg_addr ; wire Trans_Done ; wire ack_o ; wire [7:0] Rx_Data ; wire [15:0] reg_addr ; i2c_bit_shift i2c_bit_shift ( .clk (clk) , .rst_n (rst_n) , .cmd (cmd) , .go (go) , .Tx_Data (Tx_Data) , .i2c_sclk (i2c_sclk) , .i2c_sdat (i2c_sdat) , .Trans_Done (Trans_Done) , .Rx_Data (Rx_Data) , .ack_o (ack_o) ); assign reg_addr = (addr_mode) ? addr : {addr[7:0], addr[15:8]}; always @(posedge clk or negedge rst_n) if(!rst_n) begin cmd <= 6'd0; Tx_Data <= 8'd0; go <= 1'b0; rddata <= 8'd0; state <= IDLE; ack <= 0; end else begin case(state) RD_REG: begin //读寄存器状态包括读数据的四个task state <= WAIT_RD_DONE; //跳转等待读完成状态 case(cnt) 0: write_byte(WR | STA, device_id) ; //产生起始位并写入器件地址 1: write_byte(WR, reg_addr[15:8]) ; //写入寄存器地址的高8位 2: write_byte(WR, reg_addr[7:0]); //写入寄存器地址的低8位 3: write_byte(WR | STA, device_id | 8'd1); //产生起始位并写入器件地址(最后一位是1,表示读) 4: read_byte(RD | NACK | STO); //读数据并且产生NACK,STOP信号 default:; endcase end WAIT_RD_DONE:begin go <= 1'b0; //拉低go信号 if(Trans_Done) begin //等待此次数据传输完成 if(cnt <= 3) ack <= ack | ack_o; case(cnt) 0: begin cnt <= 1; state <= RD_REG; end //cnt为0时,cnt加1,跳转读寄存器状态 1: begin state <= RD_REG; if(addr_mode) //根据地址模式 cnt <= 2; //addr_more为1:16位地址 else cnt <= 3; //addr_mode位0,8位地址 end 2: begin cnt <= 3; state <= RD_REG; end 3: begin cnt <= 4; state <= RD_REG; end 4: state <= RD_REG_DONE; //cnt为4时,跳转到读完成状态 default: state <= IDLE; endcase end end RD_REG_DONE:begin RW_Done <= 1'b1; //拉高读写完成信号 rddata <= Rx_Data; //把读出的数据Rx_Data赋值给rddata state <= IDLE; //跳转到IDLE状态 end WR_REG: begin state <= WAIT_WR_DONE; //跳转到等待写完成状态 case(cnt) 0: write_byte(WR | STA, device_id); //产生起始位并写入器件地址 1: write_byte(WR, reg_addr[15:8]); //写入寄存器地址的高8位 2: write_byte(WR, reg_addr[7:0]); //写入寄存器地址的低8位 3: write_byte(WR | STO, wrdata); //写入数据并且产生停止位 default:; endcase end WAIT_WR_DONE:begin go <= 1'b0; //拉低go信号 if(Trans_Done) begin //等待此次传输数据完成 ack <= ack | ack_o; case(cnt) 0: begin cnt <= 1; state <= WR_REG; end //cnt为0时,跳转写寄存器状态 1: begin state <= WR_REG; if(addr_mode) //不同的地址模式 cnt <= 2; //决定时写入16位还是8位 else cnt <= 3; end 2: begin cnt <= 3; state <= WR_REG; end 3: state <= WR_REG_DONE; //cnt为3时,跳转写完成状态 default: state <= IDLE; endcase end end WR_REG_DONE:begin RW_Done <= 1'b1; //拉高读写完成信号 state <= IDLE; //跳转到IDLE状态 end IDLE: begin cnt <= 0; ack <= 0; RW_Done <= 1'b0; if(wrreg_req) //写请求则进入写寄存器状态 state <= WR_REG; else if(rdreg_req) //读请求则进入读寄存器状态 state <= RD_REG; else state <= IDLE; end default: state <= IDLE; endcase end //读一个字节的任务 task read_byte; input [5:0] ctrl_cmd ; //传入命令 begin cmd <= ctrl_cmd ; //将传入的命令赋值给cmd go <= 1'b1 ; //启动整个模块 end endtask //写一个字节的任务 task write_byte; input [5:0] ctrl_cmd ; //传入命令 input [7:0] wr_byte_data ; //传入要写入的8位字节数据 begin cmd <= ctrl_cmd ; //将传入的命令赋值给cmd Tx_Data <= wr_byte_data ; //将传入的8位字节数据赋值给Tx_Data go <= 1'b1 ; //启动整个模块 end endtask endmodule
仿真的tb文件:
`timescale 1ns/1ns module i2c_control_tb; reg Clk; reg Rst_n; reg wrreg_req; reg rdreg_req; reg [15:0]addr; reg addr_mode; reg [7:0]wrdata; wire [7:0]rddata; reg [7:0]device_id; wire RW_Done; wire ack; wire i2c_sclk; wire i2c_sdat; pullup PUP (i2c_sdat); i2c_ctrl i2c_control( .clk(Clk), .rst_n(Rst_n), .wrreg_req(wrreg_req), .rdreg_req(rdreg_req), .addr(addr), .addr_mode(addr_mode), .wrdata(wrdata), .rddata(rddata), .device_id(device_id), .RW_Done(RW_Done), .ack(ack), .i2c_sclk(i2c_sclk), .i2c_sdat(i2c_sdat) ); /* i2c_control i2c_control( .Clk(Clk), .Rst_n(Rst_n), .wrreg_req(wrreg_req), .rdreg_req(rdreg_req), .addr(addr), .addr_mode(addr_mode), .wrdata(wrdata), .rddata(rddata), .device_id(device_id), .RW_Done(RW_Done), .ack(ack), .i2c_sclk(i2c_sclk), .i2c_sdat(i2c_sdat) ); */ M24LC64 M24LC64( .A0(0), .A1(0), .A2(0), .WP(0), .SDA(i2c_sdat), .SCL(i2c_sclk), .RESET(~Rst_n) ); initial Clk = 1; always #10 Clk = ~Clk; initial begin Rst_n = 0; rdreg_req = 0; wrreg_req = 0; #2001; Rst_n = 1; #2000; write_one_byte(8'hA0,8'h0A,8'hd1); //#20000; write_one_byte(8'hA0,8'h0B,8'hd2); //#20000; write_one_byte(8'hA0,8'h0C,8'hd3); //#20000; write_one_byte(8'hA0,8'h0D,8'hd4); //#20000; write_one_byte(8'hA0,8'h0F,8'hd5); //#20000; read_one_byte(8'hA0,8'h0A); //#20000; read_one_byte(8'hA0,8'h0B); //#20000; read_one_byte(8'hA0,8'h0C); //#20000; read_one_byte(8'hA0,8'h0D); //#20000; read_one_byte(8'hA0,8'h0F); //#20000; $stop; end task write_one_byte; input [7:0]id; input [7:0]mem_address; input [7:0]data; begin addr = {8'd0,mem_address}; device_id = id; addr_mode = 1; wrdata = data; wrreg_req = 1; #20; wrreg_req = 0; @(posedge RW_Done); #20000; end endtask task read_one_byte; input [7:0]id; input [7:0]mem_address; begin addr = {8'd0,mem_address}; device_id = id; addr_mode = 1; rdreg_req = 1; #20; rdreg_req = 0; @(posedge RW_Done); #20000; end endtask endmodule
从仿真波形图可以看出,成功的读写了数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。