1、为什么RTL文件明明仿真没问题,Vivado例化的模块在编译电路中接地或某些信号消失
(1)检查电路的判断语句,有可能电路设置条件用于无法满足
(2)仿真尽量要将所有可能都覆盖,这可能就是随机验证的好处
(3)检查顶层模块电路端口是否冲突
(4)检查是否对同一信号多个模块赋值
2、计数器的两个用途
(1)用作模块驱动时钟分频,CLK_FREQ/I2C_FREQ = 200,计数100次翻转就可以达到250KHz
(2)用作延迟,比如说50M即0.2ns,我需要做到1s,则需要计数250000000次。
3、三态门
主要用于inout端口,当en为1时,Y作为输出,输出值取决于三态门的输入d_out;当en为0时,Y作为输入赋值给d_in。
4、内存芯片数据手册
因为AT24C64官方手册规定了数据写入芯片的完成时间最大不超过10ms。所以为了保证数据能够正确写入,单次写入数据操作完成后,最好延时10ms的时间(即保证写入时间在10ms以上,防止写入数据操作还没结束就进行写一次写操作)。
5、IIC协议模块
顶层模块、驱动模块、EEPROM读写模块、LED提示模块
(1)顶层模块
module top_e2prom(
input sys_clk,
input sys_rst_n,
output scl,
output led,
inout sda
);
parameter CLK_FREQ = 26'd50000000; //系统时钟频率
parameter I2C_FREQ = 18'd250000; //IIC_SCL的时钟频率
parameter SLAVE_ADDR = 7'b1010000; //E2PROM从机地址
parameter L_TIME = 17'd125000;
parameter BIT_CTRL = 1'b1;
parameter MAX_BYTE = 16'd256;
wire i2c_exec ;
wire i2c_rh_wl ;
wire [15:0] i2c_addr ;
wire [7:0] i2c_data_w ;
wire [7:0] i2c_data_r ;
wire i2c_done ;
wire i2c_ack ;
wire dri_clk ;
wire rw_done ;
wire rw_result ;
i2c_dri#(
.CLK_FREQ (CLK_FREQ),//系统时钟频率
.I2C_FREQ (I2C_FREQ),//IIC_SCL的时钟频率
.SLAVE_ADDR (SLAVE_ADDR) //E2PROM从机地址
)u_i2c_dri(
.clk(sys_clk),
.rst_n(sys_rst_n),
// i2c interface
.i2c_exec (i2c_exec ),
.bit_ctrl (BIT_CTRL ),
.i2c_rh_wl (i2c_rh_wl ), //rh: read high; wl: write low
.i2c_addr (i2c_addr ),
.i2c_data_w (i2c_data_w ),
.i2c_data_r (i2c_data_r ),
.i2c_done (i2c_done ),
.i2c_ack (i2c_ack ),
.scl (scl),
.sda (sda),
//user interface
.dri_clk (dri_clk)
);
e2prom_rw#(
.MAX_BYTE(MAX_BYTE)
)u_e2prom_rw(
.clk(dri_clk),
.rst_n(sys_rst_n),
//i2c interface
.i2c_data_r (i2c_data_r ),
.i2c_done (i2c_done ),
.i2c_ack (i2c_ack ),
.i2c_rh_wl (i2c_rh_wl ),
.i2c_exec (i2c_exec ),
.i2c_addr (i2c_addr ),
.i2c_data_w (i2c_data_w ),
//user interface
.rw_done (rw_done),
.rw_result (rw_result)
);
rw_result_led#(
.L_TIME(L_TIME)
)u_rw_result_led(
.clk(dri_clk),
.rst_n(sys_rst_n),
.rw_done(rw_done),
.rw_result(rw_result),
.led(led)
);
endmodule
(2)驱动模块
module i2c_dri#(
parameter CLK_FREQ = 26'd50000000, //系统时钟频率
parameter I2C_FREQ = 18'd250000, //IIC_SCL的时钟频率
parameter SLAVE_ADDR = 7'b1010000 //E2PROM从机地址
)(
input clk,
input rst_n,
// i2c interface
input i2c_exec,
input bit_ctrl,
input i2c_rh_wl, //rh: read high; wl: write low
input [15:0] i2c_addr,
input [7:0] i2c_data_w,
output reg [7:0] i2c_data_r,
output reg i2c_done,
output reg i2c_ack,
output reg scl,
inout sda,
//user interface
output reg dri_clk
);
//localparam define
localparam st_idle = 8'b0000_0001;
localparam st_sladdr = 8'b0000_0010;
localparam st_addr16 = 8'b0000_0100;
localparam st_addr8 = 8'b0000_1000;
localparam st_data_wr = 8'b0001_0000;
localparam st_addr_rd = 8'b0010_0000;
localparam st_data_rd = 8'b0100_0000;
localparam st_stop = 8'b1000_0000;
//reg define
reg sda_dir ; //dir:direct
reg sda_out ;
reg st_done ;
reg wr_flag ;
reg [ 6:0] cnt ; //scl时钟上升沿次数计数,为了准确抓取scl的高低电平
reg [ 7:0] cur_state ; //状态机当前状态
reg [ 7:0] next_state ; //状态机下一状态
//地址和数据都需要做暂存处理?
reg [15:0] addr_t ; //地址暂存
reg [ 7:0] data_r ; //读取的数据
reg [ 7:0] data_wr_t ; //I2C需写的数据的临时寄存
reg [ 9:0] clk_cnt ; //分频时钟计数,50MHz分频到1MHz
//wire define
wire sda_in;
wire [8:0] clk_divide ; //模块驱动时钟的分频系数
//三态门
assign sda = (sda_dir) ? sda_out : 1'bz;
assign sda_in = sda;
assign clk_divide = (CLK_FREQ/I2C_FREQ)>>2'd2; //模块驱动时钟的分频系数,CLK_FREQ/I2C_FREQ = 200,计数200次就可以达到250KHz,(CLK_FREQ/I2C_FREQ)>>2'd2 = 50 = 110010
//为什么不直接右移3位?因为右移2位相当于1/4倍频,是以计数clk_divide次为一个周期的标准
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作,这次是生成时钟频率。之前的是做延迟,比如说50M即0.2ns,我需要做到1s,则需要计数250000000次。
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)begin
clk_cnt <= 10'd0;
dri_clk <= 1'b0;
end
else
if(clk_cnt == clk_divide[8:1] - 1'b1) begin //clk_divide=110010, clk_divide[8:1]表示右移一位,即计数到中间时刻翻转,形成计数clk_divide次为一个周期
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else begin
clk_cnt <= clk_cnt + 1'b1;
dri_clk <= dri_clk;
end
//三段式状态机
always@(posedge dri_clk or negedge rst_n)
if(rst_n == 1'b0)
cur_state <= st_idle;
else
cur_state <= next_state;
always@(*)begin
next_state = st_idle;
case(cur_state)
st_idle:
if(i2c_exec)
next_state = st_sladdr;
else
next_state = st_idle;
st_sladdr:
if(st_done)
if(bit_ctrl)
next_state = st_addr16;
else
next_state = st_addr8;
else
next_state = st_sladdr;
st_addr16:
if(st_done)
next_state = st_addr8;
else
next_state = st_addr16;
st_addr8:
if(st_done)
if(wr_flag == 1'b0)
next_state = st_data_wr;
else
next_state = st_addr_rd;
else
next_state = st_addr8;
st_data_wr:
if(st_done)
next_state = st_stop;
else
next_state = st_data_wr;
st_addr_rd:
if(st_done)
next_state = st_data_rd;
else
next_state = st_addr_rd;
st_data_rd:
if(st_done)
next_state = st_stop;
else
next_state = st_data_rd;
st_stop:
if(st_done)
next_state = st_idle;
else
next_state = st_stop;
default:next_state = st_idle;
endcase
end
always@(posedge dri_clk or negedge rst_n)
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done <= 1'b0;
i2c_ack <= 1'b0;
cnt <= 7'b0;
st_done <= 1'b0;
data_r <= 8'b0;
i2c_data_r<= 8'b0;
wr_flag <= 1'b0;
addr_t <= 16'b0;
data_wr_t <= 8'b0;
end
else begin
st_done <= 1'b0 ;
cnt <= cnt +7'b1 ;
case(cur_state)
//空闲状态
st_idle:begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done<= 1'b0;
cnt <= 7'b0;
if(i2c_exec) begin
wr_flag <= i2c_rh_wl ;
addr_t <= i2c_addr ;
data_wr_t <= i2c_data_w;
i2c_ack <= 1'b0;
end
end
st_sladdr:
case(cnt)
7'd1 : sda_out <= 1'b0; //开始I2C,scl为高电平时sda变为低电平
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11 : scl <= 1'b0;
7'd12 : sda_out <= SLAVE_ADDR[4];
7'd13 : scl <= 1'b1;
7'd15 : scl <= 1'b0;
7'd16 : sda_out <= SLAVE_ADDR[3];
7'd17 : scl <= 1'b1;
7'd19 : scl <= 1'b0;
7'd20 : sda_out <= SLAVE_ADDR[2];
7'd21 : scl <= 1'b1;
7'd23 : scl <= 1'b0;
7'd24 : sda_out <= SLAVE_ADDR[1];
7'd25 : scl <= 1'b1;
7'd27 : scl <= 1'b0;
7'd28 : sda_out <= SLAVE_ADDR[0];
7'd29 : scl <= 1'b1;
7'd31 : scl <= 1'b0;
7'd32 : sda_out <= 1'b0; //0:写标志,1:读标志
7'd33 : scl <= 1'b1;
7'd35 : scl <= 1'b0;
7'd36 : begin
sda_dir <= 1'b0; //三态门关闭,交给从机发送响应信号
sda_out <= 1'b1; //检验响应位
end
7'd37 : scl <= 1'b1; //时钟特性
7'd38 : begin
st_done <= 1'b1;
if(sda_in == 1'b1) //是否响应,0:有应答,1:无应答。这个信号由E2PROM自己产生,连接到sda上,高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39 : begin
scl <= 1'b0; //时钟特性
cnt <= 7'd0;
end
default:;
endcase
//写高八位字地址
st_addr16:
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ; //三态门打开
sda_out <= addr_t[15]; //传送字地址
end
7'd1 : scl <= 1'b1; //开始I2C,scl为高电平时sda变为低电平
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[14]; //传送字地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[13];
7'd9 : scl <= 1'b1;
7'd11 : scl <= 1'b0;
7'd12 : sda_out <= addr_t[12];
7'd13 : scl <= 1'b1;
7'd15 : scl <= 1'b0;
7'd16 : sda_out <= addr_t[11];
7'd17 : scl <= 1'b1;
7'd19 : scl <= 1'b0;
7'd20 : sda_out <= addr_t[10];
7'd21 : scl <= 1'b1;
7'd23 : scl <= 1'b0;
7'd24 : sda_out <= addr_t[9];
7'd25 : scl <= 1'b1;
7'd27 : scl <= 1'b0;
7'd28 : sda_out <= addr_t[8];
7'd29 : scl <= 1'b1;
7'd31 : scl <= 1'b0;
7'd32 : begin
sda_dir <= 1'b0; //三态门关闭
sda_out <= 1'b1; //结束I2C
end
7'd33 : scl <= 1'b1; //时钟特性
7'd34 : begin
st_done <= 1'b1;
if(sda_in == 1'b1)
i2c_ack <= 1'b1;
end
7'd35 : begin
scl <= 1'b0; //时钟特性
cnt <= 7'd0;
end
default:;
endcase
//写低八位字地址
st_addr8:
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ; //三态门打开
sda_out <= addr_t[7]; //传送字地址
end
7'd1 : scl <= 1'b1; //开始I2C,scl为高电平时sda变为低电平
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[6]; //传送字地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[5];
7'd9 : scl <= 1'b1;
7'd11 : scl <= 1'b0;
7'd12 : sda_out <= addr_t[4];
7'd13 : scl <= 1'b1;
7'd15 : scl <= 1'b0;
7'd16 : sda_out <= addr_t[3];
7'd17 : scl <= 1'b1;
7'd19 : scl <= 1'b0;
7'd20 : sda_out <= addr_t[2];
7'd21 : scl <= 1'b1;
7'd23 : scl <= 1'b0;
7'd24 : sda_out <= addr_t[1];
7'd25 : scl <= 1'b1;
7'd27 : scl <= 1'b0;
7'd28 : sda_out <= addr_t[0];
7'd29 : scl <= 1'b1;
7'd31 : scl <= 1'b0;
7'd32 : begin
sda_dir <= 1'b0; //三态门关闭
sda_out <= 1'b1; //结束I2C
end
7'd33 : scl <= 1'b1; //时钟特性
7'd34 : begin
st_done <= 1'b1;
if(sda_in == 1'b1)
i2c_ack <= 1'b1;
end
7'd35 : begin
scl <= 1'b0; //时钟特性
cnt <= 7'd0;
end
default:;
endcase
//写数据
st_data_wr:
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ; //三态门打开
sda_out <= data_wr_t[7]; //传送字地址
end
7'd1 : scl <= 1'b1; //开始I2C,scl为高电平时sda变为低电平
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_wr_t[6]; //传送字地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_wr_t[5];
7'd9 : scl <= 1'b1;
7'd11 : scl <= 1'b0;
7'd12 : sda_out <= data_wr_t[4];
7'd13 : scl <= 1'b1;
7'd15 : scl <= 1'b0;
7'd16 : sda_out <= data_wr_t[3];
7'd17 : scl <= 1'b1;
7'd19 : scl <= 1'b0;
7'd20 : sda_out <= data_wr_t[2];
7'd21 : scl <= 1'b1;
7'd23 : scl <= 1'b0;
7'd24 : sda_out <= data_wr_t[1];
7'd25 : scl <= 1'b1;
7'd27 : scl <= 1'b0;
7'd28 : sda_out <= data_wr_t[0];
7'd29 : scl <= 1'b1;
7'd31 : scl <= 1'b0;
7'd32 : begin
sda_dir <= 1'b0; //三态门关闭
sda_out <= 1'b1; //结束I2C
end
7'd33 : scl <= 1'b1; //时钟特性
7'd34 : begin
st_done <= 1'b1;
if(sda_in == 1'b1)
i2c_ack <= 1'b1;
end
7'd35 : begin
scl <= 1'b0; //时钟特性
cnt <= 7'd0;
end
default:;
endcase
//写地址以读数据
st_addr_rd:
case(cnt)
7'd0 : begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd2 : sda_out <= 1'b0; //重新开始
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b1; //1:读
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
scl <= 1'b0;
cnt <= 7'b0;
end
default : ;
endcase
//读数据
st_data_rd: //读取数据(8 bit)
case(cnt)
7'd0: sda_dir <= 1'b0; //三态门关闭
7'd1: begin
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0;
7'd5: begin
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0;
7'd9: begin
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0;
7'd13: begin
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0;
7'd17: begin
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0;
7'd21: begin
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0;
7'd25: begin
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0;
7'd29: begin
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1; //非应答
7'd35: begin
scl <= 1'b0;
cnt <= 7'b0;
i2c_data_r <= data_r;
end
default : ;
endcase
//结束状态
st_stop: //结束I2C操作
case(cnt)
7'd0: begin
sda_dir <= 1'b1; //结束I2C
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1;
7'd15: st_done <= 1'b1;
7'd16: begin
cnt <= 7'b0;
i2c_done <= 1'b1; //向上层模块传递I2C结束信号
end
default : ;
endcase
endcase
end
endmodule
(3)EEPROM读写模块
//因为AT24C64官方手册规定了数据写入芯片的完成时间最大不超过10ms
//所以为了保证数据能够正确写入,单次写入数据操作完成后,最好延时10ms的时间。
module e2prom_rw(
input clk,
input rst_n,
//i2c interface
input [7:0] i2c_data_r,
input i2c_done,
input i2c_ack,
output reg i2c_rh_wl,
output reg i2c_exec,
output reg [15:0] i2c_addr,
output reg [7:0] i2c_data_w,
//user interface
output reg rw_done,
output reg rw_result
);
parameter WR_WAIT_TIME = 14'd5000;
parameter MAX_BYTE = 16'd256;
//reg define
reg [13:0] wait_cnt; //延时计数器
reg [1:0] flow_cnt; //状态流控制
//E2PROM 读写测试,先写后读,并比较读出的值与写入的值是否一致
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0) begin
i2c_rh_wl <= 1'b0;
i2c_exec <= 1'b0;
i2c_addr <= 16'b0;
i2c_data_w <= 8'b0;
rw_done <= 1'b0;
rw_result <= 1'b0;
wait_cnt <= 14'b0;
flow_cnt <= 2'b0;
end
else begin
i2c_exec <= 1'b0;
rw_done <= 1'b0;
case(flow_cnt)
//单次写
2'd0: begin
wait_cnt <= wait_cnt + 14'b1;
//延迟1ms
if(wait_cnt == (WR_WAIT_TIME - 14'b1)) begin
wait_cnt <= 14'b0;
//写256次后进入读状态
if(i2c_addr == MAX_BYTE) begin //i2c_data_w == MAX_BYTE会一直不能满足条件,i2c_data_w最大为255
//i2c_data_w == 16'd3,仿真文件只计数到3,所以不会出现错误
//i2c_data_w == MAX_BYTE,编译发现永远无法满足这个条件,rw_done等信号一直为初始值,所以led等信号直接接地,模块被吞
i2c_rh_wl <= 1'b1;
i2c_addr <= 16'b0;
flow_cnt <= 2'd2;
end
//继续写,直到写256次
else begin
flow_cnt <= flow_cnt + 2'b1;
i2c_exec <= 1'b1;
end
end
end
//地址和数据加1,继续写
2'd1: begin
if(i2c_done == 1'b1) begin
flow_cnt <= 2'd0;
i2c_addr <= i2c_addr + 16'b1;
i2c_data_w <= i2c_data_w + 8'b1;
end
end
//单次读
2'd2: begin
flow_cnt <= flow_cnt + 2'b1;
i2c_exec <= 1'b1;
end
//读256次
2'd3: begin
if(i2c_done == 1'b1) begin
if((i2c_addr[7:0] != i2c_data_r) || (i2c_ack == 1'b1))begin
rw_done <= 1'b1;
rw_result <= 1'b0;
end
else if(i2c_addr == (MAX_BYTE - 16'b1))begin
rw_done <= 1'b1;
rw_result <= 1'b1;
end
else begin
flow_cnt <= 2'd2;
i2c_addr <= i2c_addr + 16'b1;
end
end
end
default:;
endcase
end
endmodule
(4)LED提示模块
//****************************************Copyright (c)***********************************//
//原子哥在线教学平台:www.yuanzige.com
//技术支持:http://www.openedv.com/forum.php
//淘宝店铺:https://zhengdianyuanzi.tmall.com
//关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2023-2033
//All rights reserved
//----------------------------------------------------------------------------------------
// File name: rw_result_led
// Created by: 正点原子
// Created date: 2023年5月17日14:17:02
// Version: V1.0
// Descriptions: PL_LED0灯常亮表示读写测试正确,PL_LED0闪烁表示读写测试错误
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module rw_result_led
#(parameter L_TIME = 17'd125_000
)
(
input clk , //时钟信号
input rst_n , //复位信号
input rw_done , //结束标志
input rw_result , //E2PROM读写测试完成
output reg led //E2PROM读写测试结果 0:失败 1:成功
);
//reg define
reg rw_done_flag; //读写测试完成标志
reg [16:0] led_cnt ; //led计数
//*****************************************************
//** main code
//*****************************************************
//读写测试完成标志
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rw_done_flag <= 1'b0;
else if(rw_done)
rw_done_flag <= 1'b1;
end
//错误标志为1时PL_LED0闪烁,否则PL_LED0常亮
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led_cnt <= 17'd0;
led <= 1'b0;
end
else begin
if(rw_done_flag) begin
if(rw_result) //读写测试正确
led <= 1'b1; //led灯常亮
else begin //读写测试错误
led_cnt <= led_cnt + 17'd1;
if(led_cnt == (L_TIME - 17'b1)) begin
led_cnt <= 17'd0;
led <= ~led; //led灯闪烁
end
else
led <= led;
end
end
else
led <= 1'b0; //读写测试完成之前,led灯熄灭
end
end
endmodule