DSMC通信协议理解,以及如何在FPGA上实现DSMC从设备(2)

本篇文章尝试在FPGA上实现DSMC从设备(目前实现了单次四字节读写功能,连续读写需要稍微修改,然后加上FIFO缓存,由于项目暂时只需要单地址读写便没有做兼容设计。如果有需要的也可以互相交流学习)

由于主机测默认是LocalBus模式配置,这里也先实现LocalBus从机(抓取波形时发现主机默认的读写延时貌似是2clock)

代码和流程参考"创龙科技Tronlong"例程使用到的FPGA芯片是紫光同创的PG2L25H-6MBG325,主控芯片是瑞芯微RK3576,驱动配置参照例程(我也没看懂)在创龙例程上对接口进行稍微修改

1.模块头

复制代码
module dsmc_localbus(
//dsmc接口
input    wire    dsmc_clkp    ,
input    wire    dsmc_clkn    ,
input    wire    dsmc_csn    ,
inout    wire    dsmc_dqs0    ,
inout    wire    dsmc_dqs1    ,
inout    wire    [7:0]    dsmc_dq0    ,
inout    wire    [7:0]    dsmc_dq1    ,
//数据读写接口
output    wire    d_clk    ,//读写时钟
output    wire    d_addr    ,//读写地址
input    wire    [31:0]    d_r_data    ,//主机需要读的数据
output    wire    [31:0]    d_w_data    ,//获取到的主机数据
output    wire    d_w_en    ,//主机写使能
output    wire    d_r_en    //主机读使能
);

2.中间信号定义

复制代码
// 信号定义
wire			dsmc_clkf    ;//差分模块之后的时钟信号
wire            dsmc_csnf    ;//片选信号取反作为复位信号
wire	[7:0]    ds_datal_o    ;//DQ0总线输出,发送给上位机
wire    [7:0]    ds_datal_i    ;//DQ0总线输入,来自上位机
wire            ds_dqs0_o    ;//DQS0总线输出,发送给上位机
wire            ds_dqs0_i    ;//DQS0总线输入,来自上位机
wire	[7:0]    ds_datah_o    ;//DQ1总线输出,发送给上位机
wire    [7:0]    ds_datah_i    ;//DQ1总线输入,来自上位机
wire            ds_dqs1_o    ;//DQS1总线输出,发送给上位机
wire            ds_dqs1_i    ;//DQS1总线输入,来自上位机
reg            rw_flag    ;//DQ读写三态标志,0=上位机读数据 1=上位机写数据
reg            dqs_flag    ;//DQS读写三态标志,0=上位机读dqs 1=上位机写dqs
//data寄存器
reg            ds_rw_flag    ;//读写指令标志,0=上位机读数据 1=上位机写数据
reg    [7:0]    buf_dar0    ;
reg    [7:0]    buf_dar1    ;
reg    [7:0]    buf_dar2    ;
reg    [7:0]    buf_dar3    ;
reg    [7:0]    buf_daw0    ;
reg    [7:0]    buf_daw1    ;
reg    [7:0]    buf_daw2    ;
reg    [7:0]    buf_daw3    ;
reg    [7:0]    ds_clk_cnt    ;//上升沿计数器
reg    [31:0]    data_buf32r    ;//读取到的32bit数据缓存
reg    [15:0]    ds_addr_reg    ;//获取到的偏移地址
reg            buf_wadd_en    ;//主机写使能标志
reg            buf_radd_en    ;//主机读使能标志

3.网络赋值

复制代码
// 网络赋值
assign    dsmc_csnf    =    ~dsmc_csn    ;//片选低电平有效,这里作为复位要取反
assign    d_clk    =    dsmc_clkf    ;
assign    d_addr    =    ds_addr_reg[9:2]    ;//地址取4字节时忽略低2位,空间大小1024byet(因为我目前只需要用这么多,实际localbus最大支持32位地址索引)
assign    d_w_data    =    data_buf32r    ;
assign    d_w_en    =    (ds_clk_cnt == 8'd5) ? buf_wadd_en : 1'b0	;//读到第一个数据后就使能一次
assign    d_r_en    =    buf_radd_en    ;
// 如果需要连续读写功能,可以在这里修改读写使能标志,然后地址实现自增操作。

4.原语例化

复制代码
//-------------------------------CLOCK PORT---------------------------------------
// 差分转单端(不知道是否需要)
GTP_INBUFGDS #(.IOSTANDARD("HSTL18D_I"),.TERM_DIFF("ON"))
	clk_inbufds	(.O(dsmc_clkf),.I(dsmc_clkp),.IB(dsmc_clkn));
//-------------------------------DATA PORT---------------------------------------
// 信号三态转换
// 三态使能,T=1 时 IO 作为输入,T=0 时 IO 作为输出
genvar	i	;
generate	
	for(i=0;i<8;i=i+1)
	begin	:	dsmc_iob
		GTP_IOBUF #(.IOSTANDARD("HSTL18_I"),.SLEW_RATE("FAST"),.DRIVE_STRENGTH("8"),.TERM_DDR("ON")) 
        IOBUF_data_ds0	(.O(ds_datal_i[i]),.IO(dsmc_dq0[i]),.I(ds_datal_o[i]),.T(rw_flag));//t=0 io=i;t=1 o=io
        GTP_IOBUF #(.IOSTANDARD("HSTL18_I"),.SLEW_RATE("FAST"),.DRIVE_STRENGTH("8"),.TERM_DDR("ON")) 
        IOBUF_data_ds1	(.O(ds_datah_i[i]),.IO(dsmc_dq1[i]),.I(ds_datah_o[i]),.T(rw_flag));//t=0 io=i;t=1 o=io
        GTP_ODDR_E1 #(.GRS_EN("FALSE"),.ODDR_MODE("SAME_EDGE"),.RS_TYPE("ASYNC_RESET"))
		oddr_dq0	(.Q(ds_datal_o[i]),.D0(buf_daw0[i]),.D1(buf_daw1[i]),.CLK(dsmc_clkf),.CE(1),.RS(0));
        GTP_ODDR_E1 #(.GRS_EN("FALSE"),.ODDR_MODE("SAME_EDGE"),.RS_TYPE("ASYNC_RESET"))
		oddr_dq1	(.Q(ds_datah_o[i]),.D0(buf_daw2[i]),.D1(buf_daw3[i]),.CLK(dsmc_clkf),.CE(1),.RS(0));
	end
endgenerate
//-------------------------DQS PORT---------------------------------------------
// DQS信号处理// 作为输入,主机掩码// 作为输出,主机读数据时钟
// 从机在第五个时钟上升沿控制dq和dqs同步输出
    GTP_ODDR_E1 #(.GRS_EN("FALSE"),.ODDR_MODE("SAME_EDGE"),.RS_TYPE("ASYNC_RESET"))
	oddr_dqs0	(.Q(ds_dqs0_o),.D0(1'b1),.D1(1'b0),.CLK(dsmc_clkf),.CE(~rw_flag),.RS(0));
    GTP_ODDR_E1 #(.GRS_EN("FALSE"),.ODDR_MODE("SAME_EDGE"),.RS_TYPE("ASYNC_RESET"))
	oddr_dqs1	(.Q(ds_dqs1_o),.D0(1'b1),.D1(1'b0),.CLK(dsmc_clkf),.CE(~rw_flag),.RS(0));
    GTP_IOBUF #(.IOSTANDARD("HSTL18_I"),.SLEW_RATE("FAST"),.DRIVE_STRENGTH("8"),.TERM_DDR("ON")) 
    IOBUF_dqs0	(.O(ds_dqs0_i),.IO(dsmc_dqs0),.I(ds_dqs0_o),.T(dqs_flag));//t=0 io=i;t=1 o=io
    GTP_IOBUF #(.IOSTANDARD("HSTL18_I"),.SLEW_RATE("FAST"),.DRIVE_STRENGTH("8"),.TERM_DDR("ON")) 
    IOBUF_dqs1	(.O(ds_dqs1_i),.IO(dsmc_dqs1),.I(ds_dqs1_o),.T(dqs_flag));//t=0 io=i;t=1 o=io

5.三态使能信号控制

复制代码
//-------------------------------三态信号处理---------------------------------------
// 当前模式为localbus,非零延时访问,2个clk延时的情况下
// 读模式,从机在片选下降沿拉低dqs,从机在第五个时钟上升沿控制dq和dqs同步输出
// 写模式,从机在片选下降沿拉低dqs,第三个时钟下降沿释放dqs,第五个时钟上升沿监测dqs(可不检测)
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	rw_flag	<=	1'b1	;//主机未片选时,不使用总线
	else begin
		if((ds_clk_cnt >= 8'd4) && (ds_rw_flag == 1'b0))	rw_flag	<=	1'b0	;//读模式下,第4个下降沿从机占用总线
		else rw_flag	<=	1'b1	;
	end
end
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	dqs_flag	<=	1'b1	;//主机未片选时,不使用总线
	else begin
		if((ds_clk_cnt >= 8'd3) && (ds_rw_flag == 1'b1))	dqs_flag	<=	1'b1	;// 当写模式下,在第三个下降沿释放总线
		else	dqs_flag	<=	1'b0	;// 否则片选拉低一直占用
	end
end

6.数据字节匹配

复制代码
// 信号串行解串|解串接收
// 因为数据要在下降沿读取,因此上升沿时的总线数据要提前保存,也就是第0字节和第2字节
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	buf_dar0	<=	8'd0	;
	else	buf_dar0	<=	ds_datal_i	;// 0 
end
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	buf_dar2	<=	8'd0	;
	else	buf_dar2	<=	ds_datah_i	;// 2 
end
//信号串行解串|串行发送
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)begin
		buf_daw0	<=	8'd0	;
		buf_daw1	<=	8'd0	;
		buf_daw2	<=	8'd0	;
		buf_daw3	<=	8'd0	;
	end
	else begin
		if(ds_clk_cnt == 8'd3)begin//第一个数据寄存时机
			buf_daw0	<=	d_r_data[15:8]	;
            buf_daw1	<=	d_r_data[7:0]	;
            buf_daw2	<=	d_r_data[31:24]	;
            buf_daw3	<=	d_r_data[23:16]	;
		end
		else begin
			buf_daw0	<=	buf_daw0;
            buf_daw1	<=	buf_daw1;
            buf_daw2	<=	buf_daw2;
            buf_daw3	<=	buf_daw3;
		end
	end
end

7.沿计数

复制代码
//----------------------------------------------------------------------
// 时钟计数,计上升沿
always@(posedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	ds_clk_cnt	<=	16'd0	;
	else	ds_clk_cnt	<=	ds_clk_cnt + 1'b1	;
end

8.地址和数据使能

复制代码
//-------------------------------ADDR REG---------------------------------------
// 指令只在第dq0总线上传递
// 读指令|读写标志位.0=读  1=写
always@(negedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	ds_rw_flag	<=	1'b1	;
	else begin
		if(ds_clk_cnt == 16'd1)	ds_rw_flag	<=	buf_dar0[7]	;//第一个下降沿时,读取寄存器0的最高位
		else	ds_rw_flag	<=	ds_rw_flag	;
	end
end
// 读指令|读写地址
// 不同模式解析不同,localbus模式下
// 忽略[46:40]目标、突发类型、空间类型、FIFO合并、射频标志、区域划分
// [39:32]一个字节的突发长度
// [31:0]四个字节的起始地址,这里暂时只关注低16位
always@(negedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	ds_addr_reg	<=	16'd0	;
	else begin
		if(ds_clk_cnt == 16'd3)	ds_addr_reg	<=	{buf_dar0,ds_datal_i}	;
		else ds_addr_reg	<=	ds_addr_reg	;
	end
end
//-------------------------------DATA REG---------------------------------------
//主机写
always@(negedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	buf_wadd_en	<=	1'b0	;
	else begin
		if(ds_clk_cnt == 8'd5)	buf_wadd_en	<=	ds_rw_flag	;
		else 	buf_wadd_en	<=	1'b0	;
	end
end
always@(negedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	data_buf32r	<=	32'd0	;
	else begin
		data_buf32r[15:8]	<=	buf_dar0	;//dq0上升沿采样值,1、5
		data_buf32r[7:0]	<=	ds_datal_i	;//dq0下降沿采样值,0、4
		data_buf32r[31:24]	<=	buf_dar2	;//dq1上升沿采样值,3、7
		data_buf32r[23:16]	<=	ds_datah_i	;//dq1下降沿采样值,2、6
	end
end
//主机读
always@(negedge dsmc_clkf or negedge dsmc_csnf)begin
	if(~dsmc_csnf)	buf_radd_en	<=	1'b0	;
    else begin
		if((ds_clk_cnt >= 8'd3) && (ds_clk_cnt <= 8'd6))	buf_radd_en	<=	~ds_rw_flag;
		else    buf_radd_en	<=	1'b0	;
	end
end

endmodule
相关推荐
70asunflower7 小时前
半导体产业的经济逻辑、技术瓶颈与AI芯片格局:一份学习笔记
人工智能·笔记·学习
凉、介7 小时前
C 语言类型强转引发的隐蔽内存破坏问题分析
c语言·开发语言·笔记·学习·嵌入式
知识分享小能手7 小时前
R语言入门学习教程,从入门到精通,R语言分布式数据可视化(6)
学习·信息可视化·r语言
Robot_Nav7 小时前
Mobile ALOHA:通过低成本全身远程操作 to 实现双手机器人移动操控学习【文献解读】
学习·机器人·模仿学习·双臂移动机器人
sinat_255487817 小时前
收藏品·学习笔记
java·javascript·windows·学习·microsoft
一个脚本boy7 小时前
【渗透测试中收集信息命令并利用漏洞与提权命令总结基础版(适合新手入门学习渗透测试)】
学习·web安全·网络安全
zhangrelay7 小时前
三分钟云课实践速通--数字电子技术-数电--SimulIDE
linux·笔记·学习·ubuntu·simulide
木木_王7 小时前
嵌入式Linux学习 | 数据结构 (Day04)链表升级(进阶优化 + 柔性数组原理 + 双向循环链表完整实现 + 高频面试深挖)
linux·数据结构·学习
酿情师17 小时前
yihan:一款面向连续网页学习的智能侧边栏插件
学习·学习方法·工具·学习工具