AHB 总线接口功能模块,CPU通过驱动软件对SD HOST控制器进行访问。(内含控制寄存器,CPU通过配置寄存器,对控制器的各个模块进行控制)
功能:
1、AHB slave接口,允许CPU访问,控制sd_host的运行方式;
2、同步逻辑,SD域产生的信号同步到AHB域供软件判断;
DMA_CTRL_ADDR寄存器:
Size:32bis
Address Offset:0x44
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | dma_en | dma enable |
[3:1] | 保留 | |
[4] | dma_direaction | dma 传输方向 0:read data form ahb bus |
[15:5] | 保留 | |
[31:16] | transfer_size | dma搬运数据大小(byte) |
DMA_ADDR_ADDR寄存器:
Size:32bis
Address Offset:0x40
Read/write access:read/write
bits | name | Definition |
---|---|---|
[31:0] | dma_address | dma 数据搬移时的目标地址(这里指ahb方向(slave)的地址 |
INT_GEN_REG_ADDR寄存器:
Size:32bis
Address Offset:0x48
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | fifo_full_int_gen | |
[4] | fifo_empty_int_gen | |
[8] | dma_finish_int_gen | |
others | 保留 |
CLR_INT_REG_ADDR寄存器:
Size:32bis
Address Offset:0x4C
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | clr_fifo_full_int | |
[4] | clr_fifo_empty_int | |
[8] | clr_dma_finish_int | |
others | 保留 |
CLOCK_CONTROL_REGISTER_ADDRR寄存器:
Size:32bis
Address Offset:0x50
Read/write access:read/write
bits | name | Definition |
---|---|---|
[2] | sd_clk_enable | |
[15:8] | sd_clk_divider | sd clk 的分频系数 |
SOFTWARE_RESET_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x54
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | sd_soft_reset |
CLK_EN_SPEED_UP_ADDR寄存器:
Size:32bis
Address Offset:0x58
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | hw_stop_clk_en | |
[4] | high_speed_clk |
ARGUMENT_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x5C
Read/write access:read/write
bits | name | Definition |
---|---|---|
[31:0] | command_argument |
COMMAND_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x60
Read/write access:read/write
bits | name | Definition |
---|---|---|
[1:0] | response_type | 相响应类型 无响应 普通响应 长响应 |
[2] | data_present | |
[3] | command_enable | |
[10:5] | command_index |
BLOCK_SIZE_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x64
Read/write access:read/write
bits | name | Definition |
---|---|---|
[10:0] | block_size |
BLOCK_COUNT_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x68
Read/write access:read/write
bits | name | Definition |
---|---|---|
[31:0] | block_number_ahb |
TRANSFER_MODE_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x6C
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | data_width | |
[1] | data_direction |
READ_TIMEOUT_CONTROL_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x70
Read/write access:read/write
bits | name | Definition |
---|---|---|
[31:0] | read_to |
INTERRUPT_STATUS_MASK_REGISTER_ADDR寄存器:
Size:32bis
Address Offset:0x74
Read/write access:read/write
bits | name | Definition |
---|---|---|
[0] | response_timeout_error_interrupt_mask | |
[1] | write_data_crc_error_interrupt_mask | |
[2] | read_data_crc_error_interrupt_mask | |
[3] | tx_fifo_read_error_interrupt_mask | |
[4] | rx_fifo_write_error_interrupt_mask | |
[5] | read_to_error_interrupt_mask | |
[6] | transfer_complete_interrupt_mask | |
[7] | command_complete_interrupt_mask | |
[8] | sd_fifo_empty_interrupt_mask | |
[9] | fifo_empty_interrupt_mask | |
[10] | fifo_full_interrupt_mask | |
[11] | sd_fifo_full_interrupt_mask | |
[12] | end_command_and_response_interrupt_mask | |
[13] | dma_finish_interrupt_mask |
Signals | I/O | Width | form | to | Declaration |
---|---|---|---|---|---|
hclk | input | 1 | AHB bus | 时钟信号 | |
hrst_n | input 1 AHB bus 复位信号 | ||||
hsel | input | 1 | AHB bus | 地址decode 选中slave | |
hwrite | input | 1 | AHB bus | 1:write 0:read | |
htrans | input | 1 | AHB bus | 4种 idel busy seq nonseq | |
hburst | input | [2:0] | AHB bus | 8种 singal incr beat4/8/16 wrap4/8/16 | |
hwadata | input | [31:0] | AHB bus | 数据 | |
haddr | input | [31:0] | AHB bus | 地址 | |
hsize | input | [2:0] | AHB bus | bus的宽度 | |
hready_in | input | 1 | AHB bus | 其它slave的状态(1:其它slave IDLE 不占线) | |
hready_out | output | 1 | AHB bus | 此slave的状态,(忙:0)给仲裁其器判断使用?? | |
hresp | output | [1:0] | AHB bus | ||
hrdata | output | [31:0] | AHB bus | ||
dma_en | output | 1 | DMA | DMA使能信号 | |
dma_direc | output | 1 | DMA | 0:写fifo(从AHB到FIFO) 1:读fifo | |
transfer_size | output | [15:0] | DMA | 搬运数据的大小 (byte) | |
dma_addr | output | [31:0] | DMA | 目标地址 | |
fifo_full_int_gen | output | 1 | 中断信号产生使能(控制寄存器) | ||
fifo_empty_int_gen | output | 1 | 中断信号产生使能(控制寄存器) | ||
dma_finish_int_gen | output | 1 | 中断信号产生使能(控制寄存器) | ||
clr_fifo_full_int | output | 1 | |||
clr_fifo_empty_int | output | 1 | 清除中断信号(CLR_INT_REG_ADDR) | ||
clr_dma_finish_int | output | 1 | 清除中断信号 | ||
clr_dma_en | input | 1 | 清除dma_en 软件清零 | ||
fifo_full_int | input | 1 | fifo满中断 | ||
fifo_empty_int | input | 1 | fifo空中断 | ||
dma_finish_int | input | 1 | dma搬移结束中断 | ||
response0-3 | input | [31:0] | sd_cmd_receive | cmd模块放回的命令响应 | |
in_sd_clk | input | 1 | sd domain clk | ||
sd_fifo_empty | input | 1 | sd读fifo空 | ||
sd_fifo_full | input | 1 | sd写fifo满 | ||
sd_fifo_re | input | 1 | sd读fifo使能 sd_if中没使用?? | ||
in_end_command | input | 1 | 状态: | ||
in_end_command_and_response | input | 1 | 状态: | ||
in_transfer_complete | input | 1 | 状态: | ||
in_send_data_crc_error | input | 1 | 状态: | ||
in_receive_data_crc_error | input | 1 | 状态: | ||
in_response_timeout | input | 1 | 状态: | ||
in_cmd_current_state | input | [2:0] | sd_cmd_fsm | 状态:当前cmd状态机状态 | |
in_read_to_error | input | 1 | 状态:read time out | ||
one_bk_re_end | input | 1 | sd_data_fsm | 状态: current_state == RECEIVE_END_BIT | |
sd_clk_enable | output | 1 | sd_clk | sd_clk 分频使能 | |
hw_stop_clk | output | 1 | |||
sd_clk_divider | output | [7:0] | sd_clk 分频系数 | ||
sd_soft_reset | output | 1 | sd的软件复位 | ||
high_speed_clk | output | 1 | |||
command_argument | output | [31:0] | 命令的内容(ARGUMENT_REGISTER_ADDR) | ||
command_index | output | [5:0] | ??(COMMAND_REGISTER_ADDR) | ||
block_size | output | [10:0] | 一次多少个block | ||
block_len | output | [10:0] | 一个block多少个byte | ||
sd_op_finish | output | 1 | 用于清除FIFO ptr () | ||
block_number | output | [31:0] | |||
data_direction | output | 1 | data_fsm sd-fifo data_direction | 1:to sd card 0:to fifo | |
data_width | output | 1 | 数据位宽 | ||
read_to | output | [31:0] | read time out 时间配置 | ||
irq | output | 1 | 中断信号 | ||
out_response | output | 1 | 48bit | ||
out_longresponse | output | 1 | 136bit | ||
cmd_fsm_ready | output | 1 | cmd_fsm | cmd fsm 启动信号 | |
data_fsm_ready | output | 1 | dada_fsm | data fsm 启动信号 |
module sd_if (
//ahb bus slave singals
hclk;
hrst_n;
hsel;
hwrite;
htrans;
hburst;
hwadata;
haddr;
hsize;
hready_in;
//out
hready_out;
hresp;
hrdata;
//--------------------------
response0;
response1;
response2;
response3;
//-- host_dma
dma_en;
dma_direc;
transfer_size;
dma_addr;
fifo_full_int_gen;
fifo_empty_int_gen;
dma_finish_int_gen;
clr_fifo_full_int;
clr_fifo_empty_int;
clr_dma_finish_int;
clr_dma_en;
fifo_full_int;
fifo_empty_int;
dma_finish_int;
in_sd_clk;
sd_fifo_empty;
sd_fifo_full;
sd_fifo_re;
in_end_command;
in_end_command_and_response;
in_transfer_complete;
in_send_data_crc_error;
in_receive_data_crc_error;
in_response_timeout;
in_cmd_current_state;
in_read_to_error;
one_bk_re_end;
sd_clk_enable;
hw_stop_clk;
sd_clk_divider;
sd_soft_reset;
high_speed_clk;
command_argument;
command_index;
block_size;
block_len;
sd_op_finish;
block_number;
data_direction;
data_width;
read_to;
irq;
out_response;
out_longresponse;
cmd_fsm_ready;
data_fsm_ready;
);
// AHB signals
input hclk;
input hrst_n;
input hsel;
input hwrite;
input [1:0] htrans; //IDLE、BUSY、NONSEQ、SEQ
input [2:0] hburst; //burst类型,支持4、8、16 burst,incrementing/wrapping
input [31:0] hwadata;
input [31:0] haddr;
input [2:0] hsize; //8、16、32 bits this design
input hready_in; //other slave is no busy
output hready_out;
output [1:0] hresp;
output [31:0] hrdata;
//-- host_dma
output dma_en;
output dma_direc;
output [15:0] transfer_size; //dma transfer X byte
output [31:0] dma_addr;
// interrupt signals generater enable
output fifo_full_int_gen;
output fifo_empty_int_gen;
output dma_finish_int_gen;
//clear interrupt signals
output clr_fifo_full_int;
output clr_fifo_empty_int;
output clr_dma_finish_int;
input clr_dma_en;
input fifo_full_int;
input fifo_empty_int;
input dma_finish_int;
input [31:0] response0;
input [31:0] response1;
input [31:0] response2;
input [31:0] response3;
input in_sd_clk;
input sd_fifo_empty;
input sd_fifo_full;
input sd_fifo_re;
//state register signal
input in_end_command;
input in_end_command_and_response;
input in_transfer_complete;
input in_send_data_crc_error;
input in_receive_data_crc_error;
input in_response_timeout;
input [2:0] in_cmd_current_state;
input in_read_to_error;
input one_bk_re_end;
output sd_clk_enable;
output hw_stop_clk;
output [7:0] sd_clk_divider;
output sd_soft_reset;
output high_speed_clk;
output [31:0] command_argument;
output [5:0] command_index;
output [10:0] block_size;
output [10:0] block_len;
output sd_op_finish;
output [31:0] block_number;
output data_direction;
output data_width;
output [31:0] read_to; //read timeout
output irq; //interrupt to interrupt controler
output out_response; //respobnse 48bit
output out_longresponse; //longresponse 136bit
output cmd_fsm_ready; //fsm start signal
output data_fsm_ready; //fsm start signal
reg cmd_fsm_ready;
reg [31:0] hrdtata;
reg sd_clk_enable;
reg [7:0] sd_clk_divider;
reg sd_soft_reset;
reg hw_stop_clk_en;
reg high_speed_clk;
reg [31:0] command_argument;
reg [5:0] command_index;
reg command_enable;
reg data_present; //indicate if the current command has data transfer
reg [1:0] response_type;
reg [10:0] block_size;
reg [10:0] block_len;
reg [10:0] block_len_r;
reg [31:0] block_number;
reg data_direction;
reg data_width;
reg [31:0] read_to;
reg read_to_error;
reg dma_finish_interrupt_mask;
reg end_command_and_response_interrupt_mask;
reg sd_fifo_empty_interrupt_mask; //tx fifo empty interrupt mask
reg fifo_full_interrupt_mask; //tx fifo full interrupt mask
reg fifo_empty_interrupt_mask;
reg sd_fifo_full_interrupt_mask; //rx fifo full interrupt mask
reg command_complete_interrupt_mask;
reg transfer_complete_interrupt_mask;
reg read_to_error_interrupt_mask;
reg rx_fifo_write_error_interrupt_mask;
reg tx_fifo_read_error_interrupt_mask;
reg read_data_crc_error_interrupt_mask;
reg write_data_crc_error_interrupt_mask;
reg response_timeout_error_interrupt_mask;
reg sd_fifo_empty_r;
reg sd_fifo_full_r;
reg end_command;
reg transfer_complete;
reg send_data_crc_error;
reg receive_data_crc_error;
reg response_timeout;
reg out_response;
reg out_longresponse;
reg end_command_and_response;
//--host_dma
reg dma_en;
reg dma_direc;transfer_size;
reg [15:0] dma_addr;
reg [31:0] fifo_full_int_gen;
reg fifo_empty_int_gen;
reg dma_finish_int_gen;
reg clr_fifo_full_int;
reg clr_fifo_empty_int;
reg clr_dma_finish_int;
//-------------internal register-------------
reg hwrite_r;
reg [2:0] hsize_r;
reg [2:0] hburst_r;
reg [1:0] htrans_r;
reg [31:0] haddr_r;
reg dma_end_tp;
reg dma_end;
reg dma_end_r;
reg cmd_ready_pre;
reg hw_stop_clk;
reg [31:0] block_number_ahb;
reg [31:0] block_num_tp;
reg one_bk_re_end_tp_1;
reg one_bk_re_end_tp_2;
reg one_bk_re_end_tp_3;
reg cmd_state_send_tp1;
reg cmd_state_send_tp2;
reg cmd_state_send_tp3;
reg in_end_cmd_and_resp_tp_1;
reg in_end_cmd_and_resp_tp_2;
reg in_end_cmd_and_resp_tp_3;
reg sd_fifo_empty_tp1;
reg sd_fifo_full_tp1;
reg in_end_cmd_tp_1;
reg in_end_cmd_tp_2;
reg in_end_cmd_tp_3;
reg in_transfer_end_tp_1;
reg in_transfer_end_tp_2;
reg in_transfer_end_tp_3;
reg in_rd_to_err_tp_1;
reg in_rd_to_err_tp_2;
reg in_rd_to_err_tp_3;
reg in_send_data_crc_err_tp_1;
reg in_send_data_crc_err_tp_2;
reg in_send_data_crc_err_tp_3;
reg in_receive_data_crc_err_tp_1;
reg in_receive_data_crc_err_tp_2;
reg in_receive_data_crc_err_tp_3;
reg in_resp_timeout_tp_1;
reg in_resp_timeout_tp_2;
reg in_resp_timeout_tp_3;
reg [31:0] response0_ahb;
reg [31:0] response1_ahb;
reg [31:0] response2_ahb;
reg [31:0] response3_ahb;
reg cmd_state_send;
reg ahb_wr_reg_en;
reg ahb_rd_reg_en;
//----------------------------------------------------------
// Beginning of main code
//-------------------------------------------------------------
//------------------------------------------------------
// Generate AHB hready_out and hresp signals
//------------------------------------------------------------
//sd 的工作和这边没有关系,这里只是配置寄存器,配置完就根据寄存器内容进行工作
//这里若拉低了,DMA也不能工作
assign hready_out = 1'b1; //这里能实现单周期配置寄存器,这里没有必要拉低
assign hresp =2'b0; //respone always OK
//----------------------------------------------------------
//register AHB bus control and addr
//-----------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
hwrite_r <= 1'b0;
hsize_r <= 3'b0;
hburst_r <= 3'b0;
htrans_r <= 2'b0;
haddr_r <= 32'b0;
end
//hsel decoder 选中sd host,hread_in other slave is IDLE
else if (hsel && hready_in)
begin
hwrite_r <= hwrite;
hsize_r <= hsize;
hburst_r <= hburst;
htrans_r <= htrans;
haddr_r <= haddr;
end
else begin
hwrite_r <= 1'b0;
hsize_r <= 3'b0;
hburst_r <= 3'b0;
htrans_r <= 2'b0;
haddr_r <= 32'b0;
end
end
//-----------------------------------------------------------
// Generate ahb_wr_reg_en and ahb_rd_reg_en
//----------------------------------------------------------
//10 NOSNSEQ 11 SEQ
//ahb_wr_reg_en 写寄存器使能,ahb_rd_reg_en 读寄存器使能
assign ahb_wr_reg_en = hready_in && hwrite_r && (htrans_r == 2'b10 | htrans_r == 2'b11);
assign ahb_rd_reg_en = hsel && hready_in && !hwrite & (htrans_r == 2'b10 | htrans_r == 2'b11);
//write use hwrite_r (打拍)地址周期打完一拍刚好对上数据周期
//read use !hwrite (没打拍)
//-------------------------------------------------------------
// Generate block_len for fifo
//------------------------------------------------------------
// delay 2 clk (blcok_size hclk domian) 2d (block_len in_sd_clk block_len)
// 因为in_sd_clk 也是hclk 分频得到,这里可以保证不出现亚稳态??(他说的)
always @(posedge in_sd_clk or negedge hrst_n) begin
if (!hrst_n)
begin
block_len <= 11'd200;
block_len_r <= 11'd200;
end
else
begin
block_len_r <= block_size;
block_len <= block_len_r;
end
end
//--------------------------------------------------------------
//DMA control operation
//DMA_CTRL_ADDR : [0] dma_en
// [4] dma_direc
// [31:16] transfer_size
// [15:5]/[3:1] reserved
//-------------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
dma_en <= 1'b0;
else if (clr_dma_en)
dma_en <= 1'b0;
else if (ahb_wr_reg_en & (haddr_r[7:0]) == `DMA_CTRL_ADDR)
dma_en <= hwdata[0];
end
// dma_en 单独写(没和下面的一起),dma_en是个traig信号,只有被clr才清零
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
dma_direc <= 1'b0;
transfer_size <= 16'b0;
dma_addr <= 32'b0;
end
else if (ahb_wr_reg_en)
begin
case (haddr_r[7:0])
`DMA_CTRL_ADDR : begin
dma_direc <= hwdata[4];
transfer_size <= hwdata[31:16];
end
`DMA_ADDR_ADDR:
dma_addr <= hwdata[31:0];
endcase
end
end
//------------------------------------------------
// INT_GEN_REG_ADDR
//---------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
fifo_full_int_gen <= 1'b1;
fifo_empty_int_gen <= 1'b1;
dma_finish_int_gen <= 1'b1;
end
else if (ahb_wr_reg_en && (haddr_r[7:0]== `INT_GEN_REG_ADDR))
begin
fifo_full_int_gen <= hwdata[0];
fifo_empty_int_gen <= hwdata[4];
dma_finish_int_gen <= hwdata[8];
end
end
//-------------------------------------------------------------
//CLR_INT_REG_ADDR spec
// [0] clr_fifo_full_int
// [4] clr_fifo_empty_int
// [8] clr_dma_finish_int
// [....] reserved
//-------------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
clr_fifo_full_int <= 1'b0;
clr_fifo_empty_int <= 1'b0;
clr_dma_finish_int <= 1'b0;
end
else if (ahb_wr_reg_en && (haddr_r[7:0]== `CLR_INT_REG_ADDR))
begin
clr_fifo_full_int <= hwdata[0];
clr_fifo_empty_int <= hwdata[4];
clr_dma_finish_int <= hwdata[8];
end
else
begin
clr_fifo_full_int <= 1'b0;
clr_fifo_empty_int <= 1'b0;
clr_dma_finish_int <= 1'b0;
end
end
//-----------------------------------------------
// Generate DATA FSM start signals
//------------------------------------------------
//写准备好了(有效data_present)+fifo也写满了/commoand结束了
//sd-fifo data_direction 1:to sd card 0:to fifo
assign data_fsm_ready = data_present &&
(data_direction ? (sd_fifo_full):(in_end_command));
always @(posedge in_sd_clk or negedge hrst_n) begin
if (!hrst_n)
begin
dma_end_tp <= 1'b0;
dma_end <= 1'b0;
dma_end_r <= 1'b0;
end
else
begin
dma_end_tp <= dma_finish_int;
dma_end <= dma_end_tp;
dma_end_r <= dma_end;
end
end
//sd op finish used to clear FIFO ptr
// dirction 1:写sd 0:读sd card
//(!dma_end_r && dma_end)产生搬数据结束的脉冲
assign sd_op_finish = data_direction ? in_transfer_complete : (!dma_end_r && dma_end);
//hw stop sd_clk
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
one_bk_re_end_tp_1 <= 1'b0;
one_bk_re_end_tp_2 <= 1'b0;
one_bk_re_end_tp_3 <= 1'b0;
end
else
begin
one_bk_re_end_tp_1 <= one_bk_re_end;
one_bk_re_end_tp_2 <= one_bk_re_end_tp_1;
one_bk_re_end_tp_3 <= one_bk_re_end_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
hw_stop_clk <= 1'b0;
else if (!data_direction && hw_stop_clk_en)
begin
if (!one_bk_re_end_tp_3 && one_bk_re_end_tp_2)
//一个block 结束脉冲,停sd clk
hw_stop_clk <= 1'b1;
else if (dma_finish_int)
//dma搬数据的时候可以停时钟,搬完了就不要在停时钟了
hw_stop_clk <= 1'b0;
end
else
hw_stop_clk <= 1'b0;
end
//-----------------------------------------------
// Generate COMMAND FSM start signals
//-----------------------------------------------
assign cmd_state_send = (in_cmd_current_state == `CMD_STATE_SEND);
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
cmd_state_send_tp1 <= 1'b0;
cmd_state_send_tp2 <= 1'b0;
cmd_state_send_tp3 <= 1'b0;
end
else
begin
cmd_state_send_tp1 <= cmd_state_send;
cmd_state_send_tp2 <= cmd_state_send_tp1;
cmd_state_send_tp3 <= cmd_state_send_tp2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
cmd_ready_pre <= 1'b0;
else if (!cmd_state_send_tp3 && cmd_state_send_tp2)
//已经开始发送了 cmd_ready_pre清零,下次在配的时候再发
cmd_ready_pre <= 1'b0;
else if (command_enable && (ahb_wr_reg_en &&(haddr_r[7:0] == `ARGUMENT_REGISTER_ADDR)))
cmd_ready_pre <= 1'b1;
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
cmd_fsm_ready <= 1'b0;
else
cmd_fsm_ready <= cmd_ready_pre;
end
//-------------------------------
// Generate interrupt signal
//---------------------------------------
//mask signal mask the interrupt signal
assign irq =((dma_finish_int && !dma_finish_interrupt_mask) ||
(end_command_and_response && !end_command_and_response_interrupt_mask) ||
(sd_fifo_empty_r && !sd_fifo_empty_interrupt_mask) ||
(fifo_full_int && !fifo_full_interrupt_mask) ||
(fifo_empty_int && !fifo_empty_interrupt_mask) ||
(sd_fifo_full_r && ! sd_fifo_full_interrupt_mask)||
(end_command && !command_complete_interrupt_mask) ||
(transfer_complete && !transfer_complete_interrupt_mask) ||
(1'b0 && !rx_fifo_write_error_interrupt_mask) ||
(1'b0 && !tx_fifo_read_error_interrupt_mask) ||
(receive_data_crc_error && read_data_crc_error_interrupt_mask) ||
(send_data_crc_error && !write_data_crc_error_interrupt_mask) ||
(response_timeout && !response_timeout_error_interrupt_mask) ||
(read_to_error && !read_to_error_interrupt_mask) );
//------------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
in_end_cmd_and_resp_tp_1 <= 1'b0;
in_end_cmd_and_resp_tp_2 <= 1'b0;
in_end_cmd_and_resp_tp_3 <= 1'b0;
end
else
begin
in_end_cmd_and_resp_tp_1 <= in_end_command_and_response;
in_end_cmd_and_resp_tp_2 <= in_end_cmd_and_resp_tp_1;
in_end_cmd_and_resp_tp_3 <= in_end_cmd_and_resp_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
end_command_and_response <= 1'b0;
else if (cmd_ready_pre)
end_command_and_response <= 1'b0;
else if (!in_end_cmd_and_resp_tp_3 && in_end_cmd_and_resp_tp_2)
end_command_and_response <= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state tx fifo empty
//------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
sd_fifo_empty_tp1 <= 1'b0;
sd_fifo_empty_r <= 1'b0;
end
else begin
sd_fifo_empty_tp1 <= sd_fifo_empty;
sd_fifo_empty_r <= sd_fifo_empty_tp1;
end
end
//---------------------------------------------------
// Generate interrupt state tx fifo empty
//------------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
sd_fifo_full_tp1 <= 1'b0;
sd_fifo_full_r <= 1'b0;
end
else begin
sd_fifo_full_tp1 <= sd_fifo_full;
sd_fifo_full_r <= sd_fifo_full_tp1;
end
end
//---------------------------------------------------
// Generate interrupt command sending fifo empty
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_end_cmd_tp_1 <= 1'b0;
in_end_cmd_tp_2 <= 1'b0;
in_end_cmd_tp_3 <= 1'b0;
end
else
begin
in_end_cmd_tp_1 <= in_end_command;
in_end_cmd_tp_2 <= in_end_cmd_tp_1;
in_end_cmd_tp_3 <= in_end_cmd_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
end_command <= 1'b0;
else if (cmd_ready_pre)
end_command <= 1'b0;
else if (!in_end_cmd_tp_3 && in_end_cmd_tp_2)
end_command<= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state data transfer complete
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_transfer_end_tp_1 <= 1'b0;
in_transfer_end_tp_2 <= 1'b0;
in_transfer_end_tp_3 <= 1'b0;
end
else
begin
in_transfer_end_tp_1 <= in_transfer_complete;
in_transfer_end_tp_2 <= in_transfer_end_tp_1;
in_transfer_end_tp_3 <= in_transfer_end_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
transfer_complete <= 1'b0;
else if (cmd_ready_pre)
transfer_complete <= 1'b0;
else if (!in_transfer_end_tp_3 && in_transfer_end_tp_2)
transfer_complete<= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state data read_data_crc_error_interrupt_mask timeout
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_rd_to_err_tp_1 <= 1'b0;
in_rd_to_err_tp_2 <= 1'b0;
in_rd_to_err_tp_3 <= 1'b0;
end
else
begin
in_rd_to_err_tp_1 <= in_read_to_error;
in_rd_to_err_tp_2 <= in_rd_to_err_tp_1;
in_rd_to_err_tp_3 <= in_rd_to_err_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
read_to_error <= 1'b0;
else if (cmd_ready_pre)
read_to_error <= 1'b0;
else if (!in_rd_to_err_tp_3 && in_rd_to_err_tp_2)
read_to_error<= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state singal sending data crc error
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_send_data_crc_err_tp_1 <= 1'b0;
in_send_data_crc_err_tp_2 <= 1'b0;
in_send_data_crc_err_tp_3 <= 1'b0;
end
else
begin
in_send_data_crc_err_tp_1 <= in_send_data_crc_error;
in_send_data_crc_err_tp_2 <= in_send_data_crc_err_tp_1;
in_send_data_crc_err_tp_3 <= in_send_data_crc_err_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
send_data_crc_error <= 1'b0;
else if (cmd_ready_pre)
send_data_crc_error <= 1'b0;
else if (!in_send_data_crc_err_tp_3 && in_send_data_crc_err_tp_2)
send_data_crc_error<= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state singal receive data crc error
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_receive_data_crc_err_tp_1 <= 1'b0;
in_receive_data_crc_err_tp_2 <= 1'b0;
in_receive_data_crc_err_tp_3 <= 1'b0;
end
else
begin
in_receive_data_crc_err_tp_1 <= in_receive_data_crc_error;
in_receive_data_crc_err_tp_2 <= in_receive_data_crc_err_tp_1;
in_receive_data_crc_err_tp_3 <= in_receive_data_crc_err_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
receive_data_crc_error <= 1'b0;
else if (cmd_ready_pre)
receive_data_crc_error <= 1'b0;
else if (!in_receive_data_crc_err_tp_3 && in_receive_data_crc_err_tp_2)
receive_data_crc_error<= 1'b1;
end
//---------------------------------------------------
// Generate interrupt state singal command response timeout
//---------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
in_resp_timeout_tp_1 <= 1'b0;
in_resp_timeout_tp_2 <= 1'b0;
in_resp_timeout_tp_3 <= 1'b0;
end
else
begin
in_resp_timeout_tp_1 <= in_resp_timeout;
in_resp_timeout_tp_2 <= in_resp_timeout_tp_1;
in_resp_timeout_tp_3 <= in_resp_timeout_tp_2;
end
end
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
response_timeout <= 1'b0;
else if (cmd_ready_pre)
response_timeout <= 1'b0;
else if (!in_resp_timeout_tp_3 && in_resp_timeout_tp_2)
response_timeout<= 1'b1;
end
//---------------------------------------------------
// Config control register
//---------------------------------------------------
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
sd_clk_enable <= 1'b0;
sd_clk_divider <= 8'b0;
sd_soft_reset <= 1'b1;
command_argument <= 32'b0;
command_index <= 6'b0;
command_enable <= 1'b0;
data_present <= 1'b0;
response_type <= 2'b0;
block_size <= 11'd200;
block_number_ahb <= 32'b0;
data_direction <= 1'b0;
data_width <= 1'b0;
read_to <= 32'hffff_ffff;
dma_finish_interrupt_mask <= 1'b0;
end_command_and_response_interrupt_mask <= 1'b0;
fifo_full_interrupt_mask <= 1'b0;
command_complete_interrupt_mask <= 1'b0;
transfer_complete_interrupt_mask <= 1'b0;
read_to_error_interrupt_mask <= 1'b0;
rx_fifo_write_error_interrupt_mask <= 1'b0;
tx_fifo_read_error_interrupt_mask <= 1'b0;
read_data_crc_error_interrupt_mask <= 1'b0;
write_data_crc_error_interrupt_mask <= 1'b0;
response_timeout_error_interrupt_mask <= 1'b0;
fifo_empty_interrupt_mask <= 1'b0;
sd_fifo_full_interrupt_mask <= 1'b0;
sd_fifo_empty_interrupt_mask <= 1'b0;
hw_stop_clk_en <= 1'b0;
high_speed_clk <= 1'b0;
end
else if (ahb_wr_reg_en)
begin
case (haddr_r[7:0])
`CLOCK_CONTROL_REGISTER_ADDR: begin
sd_clk_enable <= hwdata[2];
sd_clk_divider <= hwdata[15:8];
end
`SOFTWARE_RESET_REGISTER_ADDR: begin
sd_soft_reset <= hwdata[0];
end
`CLK_EN_SPEED_UP_ADDR: begin
hw_stop_clk_en <= hwdata[0];
high_speed_clk <= hwdata[4];
end
`ARGUMENT_REGISTER_ADDR : begin
command_argument <= hwdata;
end
`COMMAND_REGISTER_ADDR : begin
command_index <= hwdata[10:5];
command_enable<= hwdata[3];
data_present <= hwdata[2];
response_type <= hwdata[1:0];
end
`BLOCK_SIZE_REGISTER_ADDR: begin
block_size <= hwdata[10:0];
end
`BLOCK_COUNT_REGISTER_ADDR: begin
block_number_ahb <= hwdata [31:0];
end
`TRANSFER_MODE_REGISTER_ADDR : begin
data_direction <= hwdata[1];
data_width <= hwdata[0];
end
`READ_TIMEOUT_CONTROL_REGISTER_ADDR: begin
read_to <= hwdata[31:0];
end
`INTERRUPT_STATUS_MASK_REGISTER_ADDR: begin
dma_finish_interrupt_mask <= hwdata[13];
end_command_and_response_interrupt_mask <= hwdata[12];
sd_fifo_full_interrupt_mask <= hwdata[11];
fifo_full_interrupt_mask <= hwdata[10];
fifo_empty_interrupt_mask <= hwdata[9];
sd_fifo_empty_interrupt_mask <= hwdata[8;
command_complete_interrupt_mask <= hwdata[7];
transfer_complete_interrupt_mask <= hwdata[6];
read_to_error_interrupt_mask <= hwdata[5];
rx_fifo_write_error_interrupt_mask <= hwdata[4];
tx_fifo_read_error_interrupt_mask <= hwdata[3];
read_data_crc_error_interrupt_mask <= hwdata[2];
write_data_crc_error_interrupt_mask <= hwdata[1];
response_timeout_error_interrupt_mask <= hwdata[0];
end
endcase
end
end
//response0-3 sd clk domain to ahb clk domain
//these data are generated in CMD_STATE_RECEIVE of cmd_current_state
//end_command_and_response can become the sync enable
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
response0_ahb <= 32'h0;
response1_ahb <= 32'h0;
response2_ahb <= 32'h0;
response3_ahb <= 32'h0;
end
else if (!in_end_cmd_and_resp_tp_3 && in_end_cmd_and_resp_tp_2)
begin
response0_ahb <= response0;
response1_ahb <= response1;
response2_ahb <= response2;
response3_ahb <= response3;
end
end
//----------------------------------------------------------
// Read state register
//-----------------------------------------------------------
always @(posedge hclk or negedge hrst_n)
begin
if (!hrst_n)
hrdata <= 32'b0;
else if (ahb_rd_reg_en)
begin
case (haddr[7:0])
`CLOCK_CONTROL_REGISTER_ADDR:
hrdata <= {16'b0,sd_clk_divider,5'b0,sd_clk_enable,2'b0};
`SOFTWARE_RESET_REGISTER_ADDR:
hrdata <= {31'b0,sd_soft_reset};
`CLK_EN_SPEED_UP_ADDR:
hrdata <= {27'b0,high_speed_clk,3'b0,hw_stop_clk_en};
`ARGUMENT_REGISTER_ADDR:
hrdata <= command_argument;
`COMMAND_REGISTER_ADDR:
hrdata <= {21'b0,command_index,1'b0,command_enable,data_present,response_type};
`BLOCK_SIZE_REGISTER_ADDR:
hrdata <= {21'b0,block_size};
`BLOCK_COUNT_REGISTER_ADDR:
hrdata <= block_number_ahb;
`TRANSFER_MODE_REGISTER_ADDR:
hrdata <= {30'b0,data_direction,data_width};
`READ_TIMEOUT_CONTROL_REGISTER_ADDR:
hrdata <= read_to;
`DMA_ADDR_ADDR :
hrdata <= dma_addr;
`DMA_CTRL_ADDR:
hrdata <= {transfer_size,11'b0,dma_direc,3'b0,dma_en};
`INT_GEN_REG_ADDR:
hrdata <= {23'b0,dma_finish_int_gen,3'b0,fifo_empty_int_gen,3'b0,fifo_full_int_gen};
`CLR_INT_REG_ADDR:
hrdata <= {23'b0,clr_fifo_empty_int,3'b0,clr_fifo_full_int,3'b0,clr_dma_finish_int};
`RESPONSE0_REGISTER_ADDR:
hrdata <= response0_ahb;
`RESPONSE1_REGISTER_ADDR:
hrdata <= response1_ahb;
`RESPONSE2_REGISTER_ADDR:
hrdata <= response2_ahb;
`RESPONSE3_REGISTER_ADDR:
hrdata <= response3_ahb;
`INTERRUPT_STATUS_REGISTER_ADDR:
begin
hrdata <= {
18'b0,
dma_finish_int,
end_command_and_response,
sd_fifo_empty_r,
fifo_full_int,
fifo_empty_int,
sd_fifo_full_r,
end_command,
transfer_complete,
read_to_error,
1'b0,
1'b0,
receive_data_crc_error,
send_data_crc_error,
response_timeout};
end
default : hrdata <= 32'h0;
endcase
end
end
//-------------------------------------------------
always @(posedge in_sd_clk or neged hrst_n) begin
if (!hrst_n)
begin
block_num_tp <= 32'h0;
block_number <= 32'g0;
end
else begin
block_num_tp <= block_number_ahb;
block_number <= block_num_tp;
end
end
endmodule
通过配置控制寄存器(软件配置),来控制模块的工作运行(硬件工作)。同时硬件在运行过程中产生各种标志信号、状态信号可以写入状态寄存器供软件进行查询。
配置寄存器示例:
always @(posedge hclk or negedge hrst_n) begin
if (!hrst_n)
begin
dma_direc <= 1'b0;
transfer_size <= 16'b0;
dma_addr <= 32'b0;
end
else if (ahb_wr_reg_en)
begin
case (haddr_r[7:0])
`DMA_CTRL_ADDR : begin
dma_direc <= hwdata[4];
transfer_size <= hwdata[31:16];
end
`DMA_ADDR_ADDR:
dma_addr <= hwdata[31:0];
endcase
end
end
信号在跨时钟域传输时会存在亚稳态的问题,单bit信号可以通过2级寄存器同步的方式大大降低亚问题出现概率(理论上不可完全消除)。
脉冲信号的同步:
1、信号通过2级寄存器同步后得到signal_r2再多打一拍signal_r3,再将signal_r3取反结果和signal_r2相与得到signal在新时钟域下的脉冲信号。
2、补充:脉冲同步器(更加通用可靠)
a.慢时钟域到快时钟域:
(1) 将 src_clk 时钟域的输入脉冲转换为 src_clk 时钟域的电平信号 src_state;
(2) 对 src_data 电平信号进行打拍(一般可打 2 拍)同步到 dst_clk 时钟域;
(3) 对 dst_clk 时钟域的电平信号进行边沿检测,产生 dst_clk 时钟域的脉冲;
部分代码:
always @(posedge src_clk or negedge src_rst_n)
begin
if(src_rst_n == 1'b0)
src_state <= 1'b0 ;
else if (src_pulse)
src_state <= ~src_state ;
end
always @(posedge dst_clk or negedge dst_rst_n)
begin
if(dst_rst_n == 1'b0)
begin
state_dly1 <= 1'b0 ;
state_dly2 <= 1'b0 ;
dst_state <= 1'b0 ;
end
else
begin
state_dly1 <= src_state ;
state_dly2 <= state_dly1;
dst_state <= state_dly2;
end
end
assign dst_pulse = dst_state ^ state_dly2 ;
b.快时钟域到慢时钟域
将快时钟域脉冲信号同步至慢时钟域可能存在以下问题:
源时钟域中的第一个脉冲和第二个脉冲间隔过短,第一个脉冲未完成同步,第 二脉冲又将状态清空,导致最终脉冲同步丢失。
于是需要引入异步握手机制,保证每个脉冲都同步成功,同步成功 后再进行下一个脉冲同步。握手原理如下: sync_req: 源时钟域同步请求信号,高电平表示当前脉冲需要同步; sync_ack: 目的时钟域应答信号,高电平表示当前已收到同步请求;
完整同步过程分为以下 4 个步骤:
(1) 同步请求产生;当同步器处于空闲(即上一次已同步完成)时,源同步脉 冲到达时产生同步请求信号 sync_req;
(2) 同步请求信号 sync_req 同步到目的时钟域,目的时钟域产生脉冲信号并将 产生应答信号 sync_ack;
(3) 同步应答信号 sync_ack 同步到源时钟域,源时钟域检测到同步应答信号 sync_ack 后,清除同步请求信号;
(4) 目的时钟域检测到 sync_req 撤销后,清除 sync_ack 应答;源时钟域将到 sync_ack 清除后,认为一次同步完成,可以同步下一个脉冲。
module HANDSHAKE_PULSE_SYNC (
src_clk , //source clock
src_rst_n , //source clock reset (0: reset)
src_pulse , //source clock pulse in
src_sync_fail , //source clock sync state: 1 clock pulse if sync fail.
dst_clk , //destination clock
dst_rst_n , //destination clock reset (0:reset)
dst_pulse //destination pulse out );
//PARA DECLARATION
//INPUT DECLARATION
input src_clk ; //source clock
input src_rst_n ; //source clock reset (0: reset)
input src_pulse ; //source clock pulse in
input dst_clk ; //destination clock
input dst_rst_n ; //destination clock reset (0:reset)
//OUTPUT DECLARATION
output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail.
output dst_pulse ; //destination pulse out
//INTER DECLARATION
wire dst_pulse ; wire src_sync_idle ; reg src_sync_fail ; reg src_sync_req ; reg src_sync_ack ; reg ack_state_dly1 ; reg ack_state_dly2 ; reg req_state_dly1 ; reg req_state_dly2 ; reg dst_req_state ; reg dst_sync_ack ;
//--========================MODULE SOURCE CODE==========================--
//--=========================================--
// DST Clock :
// 1. generate src_sync_fail;
// 2. generate sync req
// 3. sync dst_sync_ack //--=========================================--
assign src_sync_idle = ~(src_sync_req | src_sync_ack );
//report an error if src_pulse when sync busy ;
always @(posedge src_clk or negedge src_rst_n)
begin
if(src_rst_n == 1'b0)
src_sync_fail <= 1'b0 ;
else if (src_pulse & (~src_sync_idle))
src_sync_fail <= 1'b1 ;
else
src_sync_fail <= 1'b0 ;
end
//set sync req if src_pulse when sync idle ;
always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) src_sync_req <= 1'b0 ; else if (src_pulse & src_sync_idle) src_sync_req <= 1'b1 ; else if (src_sync_ack) src_sync_req <= 1'b0 ; end
always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) begin ack_state_dly1 <= 1'b0 ; ack_state_dly2 <= 1'b0 ; src_sync_ack <= 1'b0 ; end else begin ack_state_dly1 <= dst_sync_ack ; ack_state_dly2 <= ack_state_dly1 ; src_sync_ack <= ack_state_dly2 ; end end
//--=========================================--
// DST Clock :
// 1. sync src sync req
// 2. generate dst pulse
// 3. generate sync ack
//--=========================================--
always @(posedge dst_clk or negedge dst_rst_n)
begin
if(dst_rst_n == 1'b0) begin
req_state_dly1 <= 1'b0 ;
req_state_dly2 <= 1'b0 ;
dst_req_state <= 1'b0 ;
end
else begin
req_state_dly1 <= src_sync_req ;
req_state_dly2 <= req_state_dly1 ;
dst_req_state <= req_state_dly2 ;
end
end
//Rising Edge of dst_state generate a dst_pulse;
assign dst_pulse = (~dst_req_state) & req_state_dly2 ;
//set sync ack when src_req = 1 , clear it when src_req = 0 ;
always @(posedge dst_clk or negedge dst_rst_n)
begin
if(dst_rst_n == 1'b0)
dst_sync_ack <= 1'b0;
else if (req_state_dly2)
dst_sync_ack <= 1'b1;
else
dst_sync_ack <= 1'b0;
end
endmodule
通过添加屏蔽信号,可以配置是否要使用某些信号。
例如对于此设计中的中断请求信号,其中对于各种中断状态可以通过配置相应的屏蔽信号选择只有发生其中的一部分中断会产生中断请求信号,而被屏蔽的中断状态不会产生中断请求:
assign irq =((dma_finish_int && !dma_finish_interrupt_mask) ||
(end_command_and_response && !end_command_and_response_interrupt_mask) ||
(sd_fifo_empty_r && !sd_fifo_empty_interrupt_mask) ||
(fifo_full_int && !fifo_full_interrupt_mask) ||
(fifo_empty_int && !fifo_empty_interrupt_mask) ||
(sd_fifo_full_r && ! sd_fifo_full_interrupt_mask)||
(end_command && !command_complete_interrupt_mask) ||
(transfer_complete && !transfer_complete_interrupt_mask) ||
(1'b0 && !rx_fifo_write_error_interrupt_mask) ||
(1'b0 && !tx_fifo_read_error_interrupt_mask) ||
(receive_data_crc_error && read_data_crc_error_interrupt_mask) ||
(send_data_crc_error && !write_data_crc_error_interrupt_mask) ||
(response_timeout && !response_timeout_error_interrupt_mask) ||
(read_to_error && !read_to_error_interrupt_mask) );