我非常与Verilog一起使用,并花费了大量时间来解决此问题。我非常感谢您给我的任何建议/反馈!
我有这块Verilog SPI从代码,它从SPI读取200位数据并将其存储在多维数组中。我已经建立了一个测试平台,它似乎正常工作。数据恰好是SID(Commodore 64声音接口设备)的寄存器信息,SID有25个寄存器,在音乐播放时以50Hz的频率更新。因为更新到SID这么慢,我没有预料到竞争条件,但最终我想在我的设计中建立一个FIFO缓冲器...无论如何,SPI总线将非常快,因为我正在获取数据来自ESP8266,它可以为原始的SPI总线提供时钟 80MHz的。
更新代码
module spi_slave(
input ss,
input sclk,
input mosi,
input read_en,
input [4:0] addr,
output [7:0] data,
output reg data_rdy = 1'b0
);
reg [7:0] data_in;
reg [7:0] bit_cnt = 8'd0;
reg [7:0] sid_reg [24:0]; // store all SID registers
assign data = (read_en) ? sid_reg[addr] : 8'b0;
always @(posedge sclk) begin
if (!ss) begin
data_in = {data_in[6:0], mosi}; // shift left
bit_cnt = bit_cnt + 1;
if (!(bit_cnt % 8)) begin // if # bits in is divisable by 8 we just got a full byte
sid_reg[(bit_cnt/8)-1] <= data_in; // store the current value
if (bit_cnt == 8'd200) bit_cnt <= 8'd0; // once we reach 25 bytes reset the counter
end
end
end
always @(posedge ss or posedge read_en) begin
if(!bit_cnt) data_rdy = 1; // let the bus module know it's time to assert data
if(read_en) data_rdy = 0; // when read starts turn off ready
end
endmodule
我正在使用一个同样以1MHz计时SID的CPLD我还有另一个模块,它采用CPLD的20MHz时钟并对其进行适当分频。一旦我从SPI读取了200位数据,我需要在每个SID时钟周期将其发送到SID的并行地址和数据总线一个寄存器。
我计划实现一些代码,因此SPI模块可以采用addr和输出数据(模仿25字节ROM),以及输出寄存器'data_rdy',当数据准备就绪时将其设置为1。然后我写一个模块来读取每个字节并将其写入SID总线。这听起来合乎逻辑吗?如果这是解决问题的最佳方法,我正在努力。如果不是,你会怎么攻击这个?谢谢!
编辑以下是我编写的将数据写入SID总线的代码。我刚刚开始工作,但有一大堆编译器警告,我不明白......我广泛使用last_data寄存器,有人可以解释我是否应该担心这个?
WARNING:Xst:1710 - FF/Latch <last_data_9_0> (without init value) has a constant value of 0 in block <glue>. This FF/Latch will be trimmed during the optimization process.
模块:
module sid_glue(
input clk,
input sid_clk,
output sid_cs, // active low
output reg sid_rw = 1'b1, // write low
input data_rdy,
output reg [4:0] addr = 4'b0,
input [7:0] data, // incoming frame data from SPI
output reg read_en = 1'b0,
output reg [4:0] sid_addr = 5'b0,
output reg [7:0] sid_data = 8'b0
);
reg [7:0] last_data [24:0];
reg write_en = 1'b0;
// sync sid_clk to the FPGA clock using a 2-bit shift register
reg [1:0] sid_clk_r; always @(posedge clk) sid_clk_r <= {sid_clk_r[0], sid_clk};
wire sid_clk_risingedge = (sid_clk_r==2'b01);
wire sid_clk_fallingedge = (sid_clk_r==2'b10);
// same for data ready, just need to detect pos edge
reg [1:0] data_rdy_r; always @(posedge clk) data_rdy_r <= {data_rdy_r[0], data_rdy};
wire data_rdy_posedge = (data_rdy_r==2'b01);
assign sid_cs = sid_rw; // tied together for now (we don't need read atm)
always @(posedge clk) begin
if ((write_en || data_rdy_posedge) && sid_clk_risingedge) begin
if (!write_en) write_en <= 1'b1;
read_en = 1;
if ((data != last_data[addr]) || last_data[addr] === 8'bX) begin // don't write the data if it hasn't changed
sid_addr = addr; // put the address on the SID addr lines
sid_data = data; // data on the data line
sid_rw = 0;
last_data[addr] <= data; // copy data to the buffer for compare next time
end
end
else if (write_en && sid_clk_fallingedge) begin
sid_rw = 1;
read_en = 0;
addr = addr + 1; // move to the next address
if (addr == 25) begin write_en <= 0; addr = 5'b0; end // disable writes and reset counter
end
end
endmodule