在上一篇文章《FPGA的SPI从机模块实现》中,已经实现了SPI的从机模块,如何通过SPI总线与FPGA内部其他模块进行通信,是本文的主要讨论内容。

一. 新建FPGA内部DAC控制模块

这里由于手上项目需求,有两块单独DAC902核心板,其中一片DAC902的输出作为另一片DAC902的基准源输入,我们分别称它们为DACref和DACsin,顾名思义一片提供基准源电压,一片输出正弦信号或者扩展成DDS信号输出。
因此,此模块的RTL模型必须有与SPI模块通信端和外部控制DAC902的信号管脚。所以预设计这个模块为:
module dac_reg_rw_spi(clk, nrst, rec_flag, rec_data, send_flag, sending_flag, send_data, dac_clk, dacref_clk, dacref_fudu, dacsin_output);
        input clk, nrst;  //模块系统时钟、复位

//与spi模块交互引脚
input rec_flag; //spi字节接收标志
input[7:0] rec_data; //spi接收数据缓存寄存器
input sending_flag; //spi模块正在发送数据标志位
//与spi模块交互引脚
output send_flag; //dac控制模块存在需要发送数据标志位,主要负责触发spi发送
output[7:0] send_data; //spi需要发送的数据

//与外部dac通信引脚
output[11:0] dacref_fudu; //直接输出到dacref中
output[11:0] dacsin_output;
output dac_clk;
output dacref_clk;

这里假定先发送一个字节的命令,紧接着通过spi发送、接收dac控制模块所需的数据。所以定义命令字如下:

//指令代号
parameter read_dacref_fudukongzhizi=8'b00000001; //读取dacref的幅度控制字
parameter read_dacsin_xiangweikongzhizi=8'b00000010; //读取dacsin的相位控制字
parameter read_dacsin_pinlvkongzhizi=8'b00000011; //读取dacsin的频率控制字
parameter set_dacsin_pinlvkongzhizi=8'b00100001; //写入dacsin的频率控制字
parameter set_dacsin_xiangweikongzhizi=8'b00100010; //写入dacsin的相位控制字
parameter set_dacref_fudukongzhizi=8'b00100011; //写入dacref的幅度控制字
parameter set_dac_clk_pdf=8'b00100100; //设置dac时钟预分频
parameter reset_to_default=8'b11100000; //复位dac控制模块
parameter start_dac=8'b11100001; //开启dac模块
parameter stop_dac=8'b11100010; //停止dac模块

由于spi属于串行接收,一次以8位传输格式,而我们的DDS模块需要32位的频率控制字、10位的相位控制字、12位的基准源幅度控制字(具体DDS原理,由于篇幅有限,这里就不再详细介绍了),因此我们需要读取这几个寄存器或者写入这几个寄存器,需要输出、接收不等的字节长度,而dds模块处于实时运行中,所以这里需要影子寄存器的介入。

//dac配置的影子寄存器(dac运作依据的值)
reg[11:0] dacref_fudukongzhizi_shadow;
reg[31:0] dacsin_pinlvkongzhizi_shadow;
reg[9:0] dacsin_xiangweikongzhizi_shadow;
reg[3:0] dac_clk_pdf_shadow;

开始设计dac的模块吧:

reg[3:0] rw_reg_status;  //处理spi接收发送数据状态机
reg[3:0] rw_reg_status_temp; ////处理spi接收发送数据状态机(影子寄存器)
reg[7:0] rec_data_temp; //8位spi数据接收缓存
reg[3:0] delay_cnt; //发送数据延时计数器 //dac配置寄存器临时值
reg[31:0] dacsin_pinlvkongzhizi;
reg[11:0] dacref_fudukongzhizi;
reg[9:0] dacsin_xiangweikongzhizi; reg[7:0] send_data; //与myspi模块通信脚
reg send_flag; //与myspi模块通信脚
reg[2:0] byte_sended_cnt; //发送数据字节数计数器
reg[2:0] byte_received_cnt; //接收数据字节数计数器
reg dac_start_flag; //dac使能脚
reg dacref_clk; //spi通信处理状态机,需要注意的是,clk时钟频率必须为sck时钟频率约10倍以上,保证正确操作。
always @ (posedge clk or negedge nrst)
begin
if(~nrst)
begin
//初始化上述寄存器
rw_reg_status <= 4'h0;
rw_reg_status_temp <= 4'h0; //处理spi接收发送数据状态机(影子寄存器)
rec_data_temp <= 8'h00;
dacsin_pinlvkongzhizi <= 32'h00412345;
dacsin_xiangweikongzhizi <= 10'h123;
dacref_fudukongzhizi <= 12'h800;
delay_cnt <= 4'b0000;
byte_sended_cnt <= 3'b000;
send_flag <= 1'b0;
byte_received_cnt <= 3'b000;
dac_start_flag <= 1'b0;
dacref_clk <= 1'b0; dacref_fudukongzhizi_shadow <= 12'h800;
dacsin_pinlvkongzhizi_shadow <= 32'h00423456;
dacsin_xiangweikongzhizi_shadow <= 10'h200;
dac_clk_pdf_shadow <= 4'h1;
end
else
begin
case (rw_reg_status)
4'b0000: begin //从机接收指令
if(rec_flag)
begin
rec_data_temp <= rec_data;
rw_reg_status <= 4'b0001; //进入命令解析
end
end
4'b0001: begin //指令解析,跳转相应状态
case (rec_data_temp)
reset_to_default: begin
rw_reg_status <= 4'b1110;
end
read_dacref_fudukongzhizi: begin
rw_reg_status <= 4'b0011; //读dacref的幅度控制字
end
read_dacsin_xiangweikongzhizi: begin
rw_reg_status <= 4'b0010; //读dacsin的相位控制字
end
read_dacsin_pinlvkongzhizi: begin
rw_reg_status <= 4'b0110; //读dacsin的频率控制字
end
set_dacsin_pinlvkongzhizi: begin
rw_reg_status <= 4'b1101; //设置dacsin的频率控制字
rw_reg_status_temp <= 4'b0101;
end
set_dacsin_xiangweikongzhizi: begin //设置dacsin的相位控制字
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0100;
end
set_dacref_fudukongzhizi: begin //设置dacref的幅度控制字
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b1100;
end
set_dac_clk_pdf: begin //设置dac时钟预分频值
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b1001;
end
start_dac: begin
rw_reg_status <= 4'b1010;
//rw_reg_status_temp <= 4'b0000;
end
stop_dac: begin
rw_reg_status <= 4'b1011;
//rw_reg_status_temp <= 4'b0000;
end
default: begin
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
end
endcase
end
//----------------------------------------------------
4'b0011: begin //先发送幅度控制字高八位字节然后发送低八位字节
if(~sending_flag) begin //判断spi是否处于发送状态
case (byte_sended_cnt)
3'b000: begin
send_data <= {4'b0000, dacref_fudukongzhizi_shadow[11:8]};
rw_reg_status_temp <= 4'b0011;
rw_reg_status <= 4'b0111;
end
3'b001: begin
send_data <= dacref_fudukongzhizi_shadow[7:0];
rw_reg_status_temp <= 4'b0011;
rw_reg_status <= 4'b0111;
end
default: begin
byte_sended_cnt <= 3'b000;
rw_reg_status_temp <= 4'b0000;
rw_reg_status <= 4'b0000; //发送完成
end
endcase
end
else begin
send_flag <= 1'b0;
end
end
//----------------------------------------------------
4'b0010: begin
if(~sending_flag) begin //判断spi是否处于发送状态
case (byte_sended_cnt)
3'b000: begin
send_data <= {6'b000000, dacsin_xiangweikongzhizi_shadow[9:8]};
rw_reg_status_temp <= 4'b0010; //4'b0110;
rw_reg_status <= 4'b0111;
end
3'b001: begin
send_data <= dacsin_xiangweikongzhizi_shadow[7:0];
rw_reg_status_temp <= 4'b0010; //4'b0110;
rw_reg_status <= 4'b0111;
end
default: begin
rw_reg_status <= 4'b0000; //发送完成
rw_reg_status_temp <= 4'b0000;
byte_sended_cnt <= 3'b000;
end
endcase
end
else begin
send_flag <= 1'b0;
end
end
//----------------------------------------------------
4'b0110: begin
if(~sending_flag) begin //判断spi模块是否处于发送状态
case (byte_sended_cnt)
3'b000: begin
send_data <= dacsin_pinlvkongzhizi_shadow[31:24];
rw_reg_status_temp <= 4'b0110;
rw_reg_status <= 4'b0111; //4'b0100;
end
3'b001: begin
send_data <= dacsin_pinlvkongzhizi_shadow[23:16];
rw_reg_status_temp <= 4'b0110;
rw_reg_status <= 4'b0111; //4'b0100;
end
3'b010: begin
send_data <= dacsin_pinlvkongzhizi_shadow[15:8];
rw_reg_status_temp <= 4'b0110;
rw_reg_status <= 4'b0111;
end
3'b011: begin
send_data <= dacsin_pinlvkongzhizi_shadow[7:0];
rw_reg_status_temp <= 4'b0110;
rw_reg_status <= 4'b0111;
end
default: begin
rw_reg_status <= 4'b0000; //发送完成
rw_reg_status_temp <= 4'b0000;
byte_sended_cnt <= 3'b000;
end
endcase
end
else begin
send_flag <= 1'b0;
end
end
//通用状态
4'b0111: begin
//dac控制模块向spi模块提出发送请求,即生成send_flag脉冲
send_flag <= 1'b1;
if(delay_cnt == 4'b0011)
begin
delay_cnt <= 4'b0000;
rw_reg_status <= rw_reg_status_temp;
byte_sended_cnt <= byte_sended_cnt+1;
end
else
begin
delay_cnt <= delay_cnt+1;
end
end
//----------------------------------------------------
4'b0101: begin
//if(rec_flag) begin
case (byte_received_cnt)
3'b000: begin
if(rec_flag) begin //spi字节接收完成标志位
dacsin_pinlvkongzhizi[31:24] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b001;
end
end
3'b001: begin
if(rec_flag) begin
dacsin_pinlvkongzhizi[23:16] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b010;
end
end
3'b010: begin
if(rec_flag) begin
dacsin_pinlvkongzhizi[15:8] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b011;
end
end
3'b011: begin
if(rec_flag) begin
dacsin_pinlvkongzhizi[7:0] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b100;
end
end
3'b100: begin
dacsin_pinlvkongzhizi_shadow <= dacsin_pinlvkongzhizi;
byte_received_cnt <= 3'b101;
end
3'b101: begin
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
byte_received_cnt <= 3'b000;
end
endcase
//end
end
//----------------------------------------------------
 在spi接收到命令字时,下一个系统时钟clk上跳沿则进入此状态,此时rec_flag可能仍然是有效,所以会先进入4'b1101模块等待rec_flag标志位复位后再接收数据,其他状态其实大同小异,这里不一一描述。
4'b0100: begin
//if(rec_flag) begin
case (byte_received_cnt)
3'b000: begin
if(rec_flag) begin
dacsin_xiangweikongzhizi[9:8] <= rec_data[1:0];
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b001;
end
end
3'b001: begin
if(rec_flag) begin
dacsin_xiangweikongzhizi[7:0] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b010;
end
end
3'b010: begin
dacsin_xiangweikongzhizi_shadow <= dacsin_xiangweikongzhizi;
byte_received_cnt <= 3'b011;
end
3'b011: begin
rw_reg_status <= 4'b1101; //4'b0000;
rw_reg_status_temp <= 4'b0000;
byte_received_cnt <= 3'b000;
end
endcase
//end
end
//----------------------------------------------------
4'b1100: begin
//if(rec_flag) begin
case (byte_received_cnt)
3'b000: begin
if(rec_flag) begin
dacref_fudukongzhizi[11:8] <= rec_data[3:0];
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b001;
end
end
3'b001: begin
if(rec_flag) begin
dacref_fudukongzhizi[7:0] <= rec_data;
rw_reg_status <= 4'b1101;
byte_received_cnt <= 3'b010;
end
end
3'b010: begin
dacref_fudukongzhizi_shadow <= dacref_fudukongzhizi;
byte_received_cnt <= 3'b011;
end
3'b011: begin
dacref_clk <= 1'b1;
if(delay_cnt == 4'b0011)
begin
delay_cnt <= 4'b0000;
byte_received_cnt <= 3'b111;
end
else
begin
delay_cnt <= delay_cnt+1;
end
end
3'b111: begin
dacref_clk <= 1'b0;
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
byte_received_cnt <= 3'b000;
end
endcase
//end
end 4'b1101: begin
if(~rec_flag) begin //字节接收完成标志位复位等待
rw_reg_status <= rw_reg_status_temp;
end
end
//----------------------------------------------------
4'b1110: begin
dacsin_pinlvkongzhizi <= 32'h00454321;
dacsin_xiangweikongzhizi <= 10'h234;
dacref_fudukongzhizi <= 12'h321;
rw_reg_status_temp <= 4'b0000;
rw_reg_status <= 4'b1101;
end
4'b1111: begin
if(delay_cnt == 4'b0011)
begin
delay_cnt <= 4'b0000;
rw_reg_status <= 4'b0000;
end
else
begin
delay_cnt <= delay_cnt+1;
end
end
//----------------------------------------------------
4'b1010: begin
dac_start_flag <= 1'b1;
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
end
//----------------------------------------------------
4'b1011: begin
dac_start_flag <= 1'b0;
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
end
//----------------------------------------------------
4'b1001: begin
if(rec_flag) begin
dac_clk_pdf_shadow <= rec_data[3:0];
rw_reg_status <= 4'b1101;
rw_reg_status_temp <= 4'b0000;
end
end
endcase
end
end

既然dac控制模块的spi接收和发送设计好了,dds模块当然不能少,这部分就相对简单些。

//dac时钟分频模块
reg dac_clk;
reg[3:0] dac_clk_cnt; //分频,后面可以对dac_clk_cnt进行预分频处理
reg[3:0] dac_clk_pdf; //预分频
always @ (posedge clk or negedge nrst)
begin
if(~nrst)
begin
dac_clk <= 1'b0;
dac_clk_cnt <= 4'b0;
dac_clk_pdf <= 4'h1;
end
else begin
if(dac_start_flag) begin
if(dac_clk_cnt == dac_clk_pdf) begin
dac_clk_cnt <= 4'b0;
dac_clk <= ~dac_clk;
dac_clk_pdf <= dac_clk_pdf_shadow;
end
else begin
dac_clk_cnt <= dac_clk_cnt+1;
end
end
end
end //dac输出模块
assign dacref_fudu = dacref_fudukongzhizi_shadow; //直接输出到dacref中
//reg[11:0] dacsin_output;
reg[31:0] leijiazi;
reg[9:0] dac_rom_addr;
//assign dacsin_enable = nrst&dac_start_flag;
always @ (posedge clk or negedge nrst)
begin
if(~nrst) //dacsin失能
begin
leijiazi <= {dacsin_xiangweikongzhizi_shadow, 22'h000000}; //累加字存储器
dac_rom_addr <= 10'h000;
end
else //dacsin使能
begin
if(dac_start_flag) begin
leijiazi <= leijiazi+dacsin_pinlvkongzhizi_shadow;
dac_rom_addr <= leijiazi[31:22];
end
else begin
dac_rom_addr <= 10'h000;
end
end
end sin_table U3(
.clka(clk),
.addra(dac_rom_addr),
.douta(dacsin_output)
);

这里用到了名为sin_table的ROM软核,使用Block RAM组合成12位数据输出,10位数据深度(即1024个存储空间)的ROM,空间为12bits*1024。

题外话:既然提到了核,那么想当然联想到他们的分类:软核、固核和硬核三种。

软核:属于综合之前的RTL模型,只经过功能仿真,最后需要进行综合及布线后才能使用。但是不同的布线环境对其效果是不一样的,存在发送错误的可能性。

固核:带有局部规划信息的网表,对时序有一定约束后的产物,只需要通过布线工具就可以使用。

硬核:就是经过验证的设计版图,其物理版图不允许再进行修改,模块时序要求非常严格,可靠性相当高。

二. 修改spi从机模块

根据上面的dac控制模块,我们需要对先前的spi从机模块进行适当修改。
module myspi(nrst, clk, ncs, mosi, miso, sck, rec_flag, rec_data, send_flag, sending_flag, send_data);  //miso主入从出,mosi主出从入
input clk, nrst;
input ncs, mosi, sck;
input send_flag;
input[7:0] send_data;
output[7:0] rec_data;
output miso;
output sending_flag;
output rec_flag;

这样,spi模块就加入与dac控制模块的通信线路了,是不是很方便。

三. 生成顶层模块

这个主要是考虑fpga最终IO口输出情况,有点像将模块打包成一个模块,在外看FPGA内部模块相当于一个黑匣子,我们操作的时候则只关心FPGA留给我们的通信管脚就可以了。
module dac_top(clk, nrst, ncs, mosi, miso, sck, dac_clk, dacref_fudu, dacsin_output, dacref_clk);
input clk, nrst, ncs;
input mosi, sck;
output miso; output dac_clk;
output dacref_clk;
output[11:0] dacref_fudu;
output[11:0] dacsin_output; wire send_flag, rec_flag, sending_flag;
wire[7:0] rec_data;
wire[7:0] send_data; myspi U1(
.clk(clk),
.nrst(nrst),
.ncs(ncs),
.mosi(mosi),
.miso(miso),
.sck(sck),
.rec_flag(rec_flag),
.rec_data(rec_data),
.send_flag(send_flag),
.sending_flag(sending_flag),
.send_data(send_data)
); dac_reg_rw_spi U2(
.clk(clk),
.nrst(nrst),
.rec_flag(rec_flag),
.rec_data(rec_data),
.send_flag(send_flag),
.sending_flag(sending_flag),
.send_data(send_data),
.dac_clk(dac_clk),
.dacref_fudu(dacref_fudu),
.dacsin_output(dacsin_output),
.dacref_clk(dacref_clk)
); endmodule

四. ModelSim的功能验证

module dac_top_test;

	// Inputs
reg clk;
reg nrst;
reg ncs;
reg mosi;
reg sck; // Outputs
wire miso;
wire dac_clk;
wire[11:0] dacref_fudu;
wire[11:0] dacsin_output; // Instantiate the Unit Under Test (UUT)
dac_top uut (
.clk(clk),
.nrst(nrst),
.ncs(ncs),
.mosi(mosi),
.miso(miso),
.sck(sck),
.dac_clk(dac_clk),
.dacref_fudu(dacref_fudu),
.dacsin_output(dacsin_output)
); initial begin
// Initialize Inputs
clk = 0;
nrst = 0;
ncs = 1;
mosi = 0;
sck = 0; // Wait 100 ns for global reset to finish
#100;
nrst = 1;
#20;
ncs = 0; #100; mosi = 0; //先发送高位 00100011 写入频率控制字
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0; //发送 00001111
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 1; //发送 11111110
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0; //发送 00000001
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
//
#100;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
#100 sck = 1; #100 sck = 0;
//
#100; mosi = 1; //发送 11100001 启动dac控制模块
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#50000; mosi = 0; //延时50000个时间单元后再次修改频率控制字
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 1;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0;
//
#100; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0; mosi = 0;
#100; sck = 1;
#100; sck = 0;
end always #5 clk=~clk; //sck必须为clk的频率的十分之一或低于十分之一 endmodule

最终的实物图~~

如何让FPGA中的SPI与其他模块互动起来的更多相关文章

  1. FPGA中关于SPI的使用

    FPGA中关于SPI的使用 信息来源 SPI Flash的编程 最新的SPI不止有4根信号线,可以增加到支持4bit的数据宽度 SPI Flash Basics 能够扩展成4bit数据的是MOSI信号

  2. FPGA中的delay与latency

    delay和latency都有延迟的意义,在FPGA中二者又有具体的区别. latency出现在时序逻辑电路中,表示数据从输入到输出有效经过的时间,通常以时钟周期为单位. delay出现在组合逻辑电路 ...

  3. FPGA中的INOUT接口和高阻态

    除了输入输出端口,FPGA中还有另一种端口叫做inout端口.如果需要进行全双工通信,是需要两条信道的,也就是说需要使用两个FPGA管脚和外部器件连接.但是,有时候半双工通信就能满足我们的要求,理论上 ...

  4. FPGA中的时序分析(四)

    常用约束语句说明 关于Fmax      上述是实现Fmax的计算公式,clock skew delay的计算如下图, 就是两个时钟的差值.到头来,影响Fmax的值的大小就是组合逻辑,而Fmax是针对 ...

  5. 【转】关于FPGA中建立时间和保持时间的探讨

      时钟是整个电路最重要.最特殊的信号,系统内大部分器件的动作都是在时钟的跳变沿上进行, 这就要求时钟信号时延差要非常小, 否则就可能造成时序逻辑状态出错:因而明确FPGA设计中决定系统时钟的因素,尽 ...

  6. FPGA的SPI从机模块实现

    一. SPI总线协议         SPI(Serial Peripheral Interface)接口,中文为串行外设接口.它只需要3根线或4根线即可完成通信工作(这里讨论4根线的情况).     ...

  7. FPGA中浮点运算实现方法——定标

    有些FPGA中是不能直接对浮点数进行操作的,仅仅能採用定点数进行数值运算.对于FPGA而言,參与数学运算的书就是16位的整型数,但假设数学运算中出现小数怎么办呢?要知道,FPGA对小数是无能为力的,一 ...

  8. FPGA中改善时序性能的方法_advanced FPGA design

    本文内容摘自<advanced FPGA design>对应中文版是 <高级FPGA设计,结构,实现,和优化>第一章中的内容 FPGA中改善时序,我相信也是大家最关心的话题之一 ...

  9. Altera FPGA中的pin进一步说明

    最近END china上的大神阿昏豆发表了博文 <FPGA研发之道(25)-管脚>,刚好今天拿到了新书<深入理解Altera FPGA应用设计>第一章开篇就讲pin.这里就两者 ...

随机推荐

  1. C# Struct结构体

    C#中结构类型和类类型在语法上非常相似,他们都是一种数据结构,都可以包括数据成员和方法成员. 结构和类的区别: 1.结构是值类型,它在栈中分配空间:而类是引用类型,它在堆中分配空间,栈中保存的只是引用 ...

  2. css案例学习之span边框实现的特殊效果

    bottom left bottom right top left top right 配合颜色来使用,实现一些神奇的效果 #menu a span{ height:; width:; /*borde ...

  3. LRU Cache 解答

    Question Design and implement a data structure for Least Recently Used (LRU) cache. It should suppor ...

  4. Summary Ranges 解答

    Question Given a sorted integer array without duplicates, return the summary of its ranges. For exam ...

  5. 那些年的那些事CISC和RISC发展中的纠缠

    本文来自http://www.cnbeta.com/articles/224544.htm ARM.ARM.ARM,没错ARM仿佛一夜之间就火了,平板.手机等领域随处可见它的影子,甚至已经有人预言未来 ...

  6. AC大牛经典语录

    超经典:     1. 为了世界的和平,为了女生的安全,我拼命做题,做题,做题! 2. A ac a day, keeps the doctor away! 3. from good to great ...

  7. 如何统一删除word中的超链接

    [摘要] 我们从别处拷贝文字,或从网上复制的文字,里面有很多超级链接,如何可以批量删除这些链接呢?这里介绍两种批量删除链接的方法. [正文] 方法一:使用快捷键删除超链接 有个神奇的快捷键,可以帮我们 ...

  8. Gulp及组件安装构建

    Gulp 是一款基于任务的设计模式的自动化工具,通过插件的配合解决全套前端解决方案,如静态页面压缩.图片压缩.JS合并.SASS同步编译并压缩CSS.服务器控制客户端同步刷新. Gulp安装 全局安装 ...

  9. MVC学习 (二) Razor语法

    MVC的Model层我理解与三层架构的Molde没有区别,都是作为各个层之间的数据沟通桥梁.但是关于Control和View都有一些与传统webform不同的特性. 这里先学习View里所用到的Raz ...

  10. ORACLE OCP认证

    基本情况介绍 Oracle产品非常多,这里说的是Oracle数据库认证体系. Oracle数据库认证体系包括3层,分别是OCA(助理),OCP(专家),OCM(大师) 一般情况下,需一级一级认证,也就 ...