SOC设计之 数据回路从 DMA到 FIFO再到BRAM 再到FIFO 再写回DMA
基本没问题的回路设计
从 DMA出发将数据传递到 FIFO 再 写到 自定义的 RTL文件中 再写到 BRAM 再到 自定义的RTL文件 再到 FIFO 再写回DMA
block design 的 设计连接 可以参考我上一个文件的设计
下面介绍两个control1.v
control1.v
//11_11 this is new building for real control11
module control11 #(
parameter FIFO_CONTROL1_DATA = 32 ,
parameter FIFO_CONTROL1_KEEP = 4 ,
parameter ADDRESSA_WIDTH = 17 ,
parameter ONECONTROL = 8 ,
parameter DINA_WIDTH = 32
)(
// connect fifo
input sys_clk ,
input sys_rst_n ,
input [FIFO_CONTROL1_KEEP - 1 : 0] control1_tkeep , // 4
input [FIFO_CONTROL1_DATA - 1 : 0] control1_tdata ,
input control1_tlast ,
output reg control1_tready ,
input control1_tvaild ,
// next is connect BRAM
output reg [ADDRESSA_WIDTH - 1 : 0] addressa ,
output reg [DINA_WIDTH - 1 : 0] dina ,
output reg ena ,
output reg wea ,
output reg [ADDRESSA_WIDTH : 0] finish
);
//===================================================================\\
// internal signal \\
//====================================================================\\
//wire [FIFO_CONTROL1_KEEP - 1 : 0] tkeep ;
//assign tkeep = control1_tkeep ;
// we can use tlast tell me when the send is finish and the last addressa is what
reg [2 : 0] mode ;
//reg [DINA_WIDTH - 1 : 0] data_keep ;
//reg [ADDRESSA_WIDTH - 1 : 0] addressa_keep ;
// how to put control1_tdata and keep together control1_tkeep
wire [ONECONTROL - 1 : 0] control1_tdata_0 ; // 7- 0
wire [ONECONTROL - 1 : 0] control1_tdata_1 ; // 15 - 8
wire [ONECONTROL - 1 : 0] control1_tdata_2 ; // 23 - 16
wire [ONECONTROL - 1 : 0] control1_tdata_3 ; // 31 - 24
wire [FIFO_CONTROL1_DATA - 1 : 0] control1_tdata_total ;
assign control1_tdata_0 = (control1_tkeep[0] == 1) ? control1_tdata[7 : 0] : 8'b0 ;
assign control1_tdata_1 = (control1_tkeep[1] == 1) ? control1_tdata[15 : 8] : 8'b0 ;
assign control1_tdata_2 = (control1_tkeep[2] == 1) ? control1_tdata[23 : 16] : 8'b0 ;
assign control1_tdata_3 = (control1_tkeep[3] == 1) ? control1_tdata[31 : 24] : 8'b0 ;
assign control1_tdata_total = {control1_tdata_3,control1_tdata_2,control1_tdata_1,control1_tdata_0} ;
// we need get the number delay
//reg write ;
//===================================================================\\
// next is main code \\
//====================================================================\\
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 0)
begin
control1_tready <= 1 ;
addressa <= 0 ;
dina <= 0 ;
ena <= 0 ;
wea <= 0 ;
mode <= 0 ;
// write <= 0 ;
end
else
begin
case(mode)
3'b000 :
begin
if((control1_tvaild && control1_tready && control1_tdata_total) == 0 )
begin
addressa <= addressa ;
dina <= dina ;
ena <= 0 ;
wea <= 0 ;
mode <= 0 ;
// write <= 0 ;
end
else
begin
// addressa <= addressa ;
//dina <= control1_tdata_total ;
//ena <= 1 ;
// wea <= 1 ;
// write <= 0 ;
mode <= 3'b001;
end
end
3'b001 : // send addressa and dina
begin
control1_tready <= 0 ;
addressa <= addressa + 4 ;
dina <= control1_tdata_total ;
ena <= 1 ;
wea <= 1 ;
// write <= 0 ;
mode <= 3'b010 ;
end
3'b010 :
begin
if(control1_tlast == 1)
begin
addressa <= addressa ;
dina <= dina ;
ena <= 0 ;
wea <= 0 ;
mode <= 0 ;
// write <= 0 ;
end
else
begin
control1_tready <= 1 ;
addressa <= addressa ;
dina <= dina ;
ena <= 0 ;
wea <= 0 ;
// write <= 1 ;
mode <= 3'b001 ;
end
end
default :
begin
control1_tready <= 1 ;
addressa <= addressa ;
dina <= dina ;
ena <= 0 ;
wea <= 0 ;
mode <= 0 ;
end
endcase
end
end
// we can use finish send the adress and tlast
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 0)
begin
finish <= 0 ;
end
else if(control1_tlast == 1)
begin
finish <= {addressa,control1_tlast} ;
end
else
finish <= 0 ;
end
endmodule
control2.v
// this is 11_11 new building for control22
module control22 #(
parameter FINISH_WIDTH = 17 ,
parameter DATA_WIDTH = 32 ,
parameter TKEEP_WIDTH = 4
)(
input sys_clk ,
input sys_rst_n ,
// from control11
input [FINISH_WIDTH : 0] finish_control22 ,
// connect BRAM
output reg enb ,
output reg [FINISH_WIDTH - 1 : 0] addressb ,
input [DATA_WIDTH - 1 : 0] doutb ,
// connect FIFO
output reg [DATA_WIDTH - 1 : 0] tdata ,
output reg [TKEEP_WIDTH - 1 : 0] tkeep ,
output tlast ,
output reg tvaild ,
input tready
);
//===========================================================\\
// internal signal \\
//===========================================================\\
//assign tkeep = {TKEEP_WIDTH{1'b1}} ;
reg [2 : 0] mode ;
//reg [FINISH_WIDTH - 1 : 0] addressb_keep ;
//===========================================================\\
// next is main code \\
//============================================================\\
// finish_control22
// doutb
// tready
//tlast
assign tlast = (addressb >= finish_control22[FINISH_WIDTH : 1] && addressb != 0) ? 1 : 0 ;
//assign tvaild = (finish_control22[0] == 1 && (addressb != finish_control22[FINISH_WIDTH : 1])) ? 1 : 0 ;
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 0)
begin
enb <= 0 ;
addressb <= 0 ;
tdata <= 0 ;
tvaild <= 0 ;
mode <= 0 ;
tkeep <={TKEEP_WIDTH{1'b0}} ;
end
else
begin
case(mode)
3'b000 :
begin
if( (tready && finish_control22[0]) == 0)
begin
enb <= 0 ;
tdata <= 0 ;
addressb <= addressb ;
mode <= 0 ;
tvaild <= 0 ;
tkeep <={TKEEP_WIDTH{1'b0}} ;
end
else
begin
tvaild <= 1 ;
mode <= 3'b001 ;
// tlast <=(addressb == finish_control22[FINISH_WIDTH : 1] && addressb != 0) ? 1 : 0 ;
//tkeep <={TKEEP_WIDTH{1'b1}} ;
end
end
3'b001 :
begin
if((tready && tvaild) == 1)
begin
tvaild <= 0 ;
enb <= 1 ;
addressb <= addressb + 4 ;
tdata <= doutb ;
tkeep <={TKEEP_WIDTH{1'b1}} ;
mode <= 3'b010 ;
end
else
begin
mode <= 3'b000 ;
end
end
3'b010 :
// begin
// if(tlast == 1)begin
// enb <= 0 ;
// addressb <= addressb ;
// tdata <= tdata ;
// tvaild <= 0 ;
// mode <= 3'b010 ;
// tkeep <={TKEEP_WIDTH{1'b1}} ;
// end
// else
begin
enb <= 0 ;
addressb <= addressb ;
tdata <= tdata ;
tvaild <= 1 ;
mode <= 3'b001 ;
tkeep <={TKEEP_WIDTH{1'b1}} ;
end
// end
default :
begin
tvaild <= 0 ;
enb <= 0 ;
addressb <= addressb ;
tdata <= tdata ;
mode <= 0 ;
tkeep <={TKEEP_WIDTH{1'b0}} ;
end
endcase
end
end
endmodule
下面是SDK的 C语言代码
#include "xparameters.h" //包含vivado所导出的信息包含vivado的基地址
#include "xil_printf.h" //调用打印函数的时候,需要引用这个头文件
#include "ff.h" //使用FATFS库函数时需要
#include "xstatus.h" //需要的一些关键信息
#include"stdio.h" // 专业写法
#include "xdevcfg.h"
#include "xil_io.h"
/下面讲述关于 DMA
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "sleep.h"
/************************** Variable Definitions *****************************/
static FATFS fatfs; //文件系统
//#define FILE_NAME "parameter_512_512.txt" //定义文件名
#define FILE_NAME "zxs1.txt"
#define FILE_READ_NAME "zzxxss.txt"
static XAxiDma axidma; //XAxiDma实例
static XScuGic intc; //中断控制器的实例
volatile int tx_done; //发送完成标志
volatile int rx_done; //接收完成标志
volatile int error; //传输出错标志
/************************** Constant Definitions *****************************/
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) //0x01100000
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00700000) //0x01800000
#define RESET_TIMEOUT_COUNTER 100000000 //复位时间
//复位时间设置的如此之长 是为了保证在下面的刷新DMA的用时 虽然我之前小数据只写了1000
//#define TEST_START_VALUE 0x0 //测试起始值
//#define MAX_PKT_LEN 0x100 //发送包长度 对应十进制是256 DMA传递的单次包长最大是256
//指的是stream流的包长度 不是突发长度
#define MAX_PKT_LEN 5000 //发送包长度 163840
//指的是单次 DMA 传输的包长度 因为数据一共有 16384 × 8 再加上 转
#define len 5000 //传输数据的大小
//函数声明
int platform_init_fs();
int sd_mount() ; // 挂载SD卡
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len); //SD卡写数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len) ; //SD卡读数据
static void tx_intr_handler(void *callback);
static void rx_intr_handler(void *callback);
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id);
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id);
// main函数
int main()
{
int status; // 当前状态
int i = 0 ; // 传递函数使用的顺序i t
int t = 0 ;
u32 value = 0 ;
u32 *tx_buffer_ptr;
u32 *rx_buffer_ptr;
XAxiDma_Config *config;
tx_buffer_ptr = (u32 *) TX_BUFFER_BASE;
rx_buffer_ptr = (u32 *) RX_BUFFER_BASE;
// 如果分配成功:则返回指向被分配内存空间的指针,不然返回指针NULL 。同时,当内存不再使用的时候,应使用free()函数将内存块释放掉。
// 关于:void*,表示未确定类型的指针,c,
//c++规定void*可以强转为任何其他类型的指针,关于void还有一种说法就是其他任何类型都可以直接赋值给它,无需进行强转,但是反过来不可以 。
//它被用来在运行时而不是编译时分配内存。因此,如果您的数据数组基于来自用户,数据库,文件等的某种输入,那么一旦知道所需大小,就必须使用malloc。
//malloc 在堆上开辟空间
char *ch_data = NULL; //SD卡读到的数据
char *ddr_rd_data = NULL; //重新写回SD卡的数据
ch_data = malloc(len);
if(ch_data == NULL) printf("ERRIOR \r\n");
ddr_rd_data = malloc(len);
if(ddr_rd_data == NULL) printf("ERRIOR \r\n");
// 两个东西 一个 放 开始内容 另一个 写结束
//常规
status = sd_mount(); //挂载SD卡----调用函数
///
if(status != XST_SUCCESS){
xil_printf("Failed to open SD card!\n");
return 0;
}
else
xil_printf("Success to open SD card!\n");
//为了展示效果 我决定写一下sd卡
//len = strlen(src_str); //计算字符串长度
//SD卡写数据
//sd_write_data(FILE_NAME,(u32)src_str,len);
sd_read_data(FILE_NAME,(u32)ch_data,len);
//
printf("Successful write SD in Pc ! \r\n");
usleep(1);
// sd_read_data(FILE_NAME,(u32)dest_str,len); //读数据进入dest_str
//printf("Successful Read SD ! \r\n");
//到目前我们已经把数据放到了dest_str
// 写数据
for(i=0; i<len; i++)
{
Xil_Out32(TX_BUFFER_BASE+i*4,ch_data[i]);
}
i=0;
usleep(1);
//读数据
printf("Successful Write SD in DDR! \r\n");
xil_printf("--- Entering main() --- \r\n");
printf("DMA initialization succeeded ! \r\n");
config = XAxiDma_LookupConfig(DMA_DEV_ID); //查找DMA的配置信息
if (!config) {
xil_printf("No config found for %d\r\n", DMA_DEV_ID);
return XST_FAILURE;
}
//初始化DMA引擎
status = XAxiDma_CfgInitialize(&axidma, config);
if (status != XST_SUCCESS) {
xil_printf("Initialization failed %d\r\n", status);
return XST_FAILURE;
}
if (XAxiDma_HasSg(&axidma)) {
xil_printf("Device configured as SG mode \r\n");
return XST_FAILURE;
}
status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
if (status != XST_SUCCESS) {
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
//初始化标志信号
tx_done = 0;
rx_done = 0;
error = 0;
usleep(10);
//数据地址都确定了
Xil_DCacheFlushRange((UINTPTR)(tx_buffer_ptr), len); //刷新Data Cache
// may be this is wrong
printf("DDR send Read data5 \n");
status = XAxiDma_SimpleTransfer(&axidma, (u32) (tx_buffer_ptr),
len, XAXIDMA_DMA_TO_DEVICE);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_DCacheFlushRange((UINTPTR) (tx_buffer_ptr), len); //刷新Data Cache
printf("DDR send Read data6 \n");
usleep(10) ;
//DMA写通道开启
status = XAxiDma_SimpleTransfer(&axidma, (u32) (rx_buffer_ptr),
len, XAXIDMA_DEVICE_TO_DMA);
首先是器件例化的指针 ,起始或目的地址 ,传输长度 , DMA传输方向
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
printf("DDR send Read data7 \n");
Xil_DCacheFlushRange((UINTPTR) (rx_buffer_ptr), len); //刷新Data Cache
while (!tx_done && !rx_done && !error);
printf("DDR get Read data \n");
//传输出错
if (error) {
xil_printf("Failed test transmit%s done, "
"receive %s done\r\n", tx_done ? "" : "tx_done not",
rx_done ? "" : "rx_done not");
goto Done;
}
usleep(10000);
//传输完成,检查数据是否正确
xil_printf("Successfully ran AXI DMA Loop\r\n");
disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
Done: xil_printf("--- Exiting main() --- \r\n");
for(i=0; i<len; i++)
{
value = Xil_In32(RX_BUFFER_BASE+i*4);
ddr_rd_data[t] = value;
t++;
}
usleep(1);
sd_write_data(FILE_READ_NAME,(u32)ddr_rd_data,len);
printf("SD Write Successfully ! \r\n");
free(ch_data);
free(ddr_rd_data);
return XST_SUCCESS;
}
//初始化文件系统
int platform_init_fs()
{
FRESULT status;
TCHAR *Path = "0:/";
BYTE work[FF_MAX_SS];
//注册一个工作区(挂载分区文件系统)
//在使用任何其它文件函数之前,必须使用f_mount函数为每个使用卷注册一个工作区
status = f_mount(&fatfs, Path, 1); //挂载SD卡
if (status != FR_OK) {
xil_printf("Volume is not FAT formated; formating FAT\r\n");
//格式化SD卡
status = f_mkfs(Path, FM_FAT32, 0, work, sizeof work);
if (status != FR_OK) {
xil_printf("Unable to format FATfs\r\n");
return -1;
}
//格式化之后,重新挂载SD卡
status = f_mount(&fatfs, Path, 1);
if (status != FR_OK) {
xil_printf("Unable to mount FATfs\r\n");
return -1;
}
}
return 0;
}
//挂载SD(TF)卡
int sd_mount()
{
FRESULT status;
//初始化文件系统(挂载SD卡,如果挂载不成功,则格式化SD卡)
status = platform_init_fs();
if(status){
xil_printf("ERROR: f_mount returned %d!\n",status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
//SD卡写数据
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT bw; //f_write函数返回已写入的字节数
//打开一个文件,如果不存在,则创建一个文件
f_open(&fil,file_name,FA_CREATE_ALWAYS | FA_WRITE);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil, 0);
//向文件中写入数据
f_write(&fil,(void*) src_addr,byte_len,&bw);
//关闭文件
f_close(&fil);
return 0;
}
//SD卡读数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT br; //f_read函数返回已读出的字节数
//打开一个只读的文件
f_open(&fil,file_name,FA_READ);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil,0);
//从SD卡中读出数据
f_read(&fil,(void*)src_addr,byte_len,&br);
//关闭文件
f_close(&fil);
return 0;
}
//DMA TX中断处理函数
static void tx_intr_handler(void *callback)
{
int timeout;
u32 irq_status;
XAxiDma *axidma_inst = (XAxiDma *) callback;
//读取待处理的中断
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
//确认待处理的中断
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
//Tx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Tx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
tx_done = 1;
}
//DMA RX中断处理函数
static void rx_intr_handler(void *callback)
{
u32 irq_status;
int timeout;
XAxiDma *axidma_inst = (XAxiDma *) callback;
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
//Rx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Rx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
rx_done = 1;
}
//建立DMA中断系统
// @param int_ins_ptr是指向XScuGic实例的指针
// @param AxiDmaPtr是指向DMA引擎实例的指针
// @param tx_intr_id是TX通道中断ID
// @param rx_intr_id是RX通道中断ID
// @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id)
{
int status;
XScuGic_Config *intc_config;
//初始化中断控制器驱动
intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == intc_config) {
return XST_FAILURE;
}
status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
intc_config->CpuBaseAddress);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
//设置优先级和触发类型
XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
//为中断设置中断处理函数
status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
(Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
(Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
XScuGic_Enable(int_ins_ptr, tx_intr_id);
XScuGic_Enable(int_ins_ptr, rx_intr_id);
//启用来自硬件的中断
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
(void *) int_ins_ptr);
Xil_ExceptionEnable();
//使能DMA中断
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
return XST_SUCCESS;
}
//此函数禁用DMA引擎的中断
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id)
{
XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
}