quratus 乒乓
先看一下RAM ipcore的特征,在配置时先看一下配置方式就是有锁存器


在生成的ipcore中
altsyncram_component.outdata_reg_a ="UNREGISTERED",就是无寄存器
altsyncram_component.outdata_reg_a ="CLOCK0",就是有寄存器
发现连了时钟线的就是有锁存器,没连时钟线的就是没锁存器。很可能有时钟触发的就是always@(edge clk),没有时钟就是assign A=B,所以有时钟触发里有锁存器,没有就是总线输入输出,没锁存器。
双口RAM,不选择输出有寄存器的使用方法:
1. 写使能,第一个写地址,第一个写数据在同步赋值。
2. 读使能一直有效,读地址在和读出第一个数据间有一个时钟延时,那么第一个数据
读出时正好读地址开始递增1。
虽然读出数据没有寄存器,但读地址有寄存器,所以读地址比读出数据快一个时钟;而写RAM时,地址和数据都有寄存器,所以两个是同步赋值,触发条件就是这里的写使能信号,所以三者要同时赋值,在写使能赋值下一个时钟,读进去地址和数据锁存器里的值。
自己编写pingpang
读出ROM里1024个点,用两个64 words的RAM做乒乓读出
随着第一个数据有效信号,出现了第一个数据。
写RAM部分:
1. 初始的复位状态:写片选默认为第一个片,写地址为0;
2. 第一个数据进来,写地址判断数据有效信号自增1,由于是时钟触发,地址寄存器
出现锁存器,要到下一个时钟才递增,故地址与读入数据同步对应;
3. 当地址为最后一个地址时,实时触发一个flag,时钟采集这个flag,下一个时钟采
集到,地址回到0,此时片选RAM写使能切换到下一块RAM,如此循环反复。
读RAM部分:
1. 初始状态:默认读第一个芯片,读地址为0,写使能一直有效;
2. 时钟采集第一次写末地址的flag时,写地址使能(写地址使能不是写使能),此时
已经是写地址归0,读地址为0;
3. 写地址采集到写地址使能,此时已经是又下一个时钟,此时写地址为1,读地址自
增1,也为1,读出第一个片子的第一个数据,而第二个片子准备好了写入数据和写入数据地址,实际写入的也是第一个数据,此时读地址和写地址同步;
4. 读地址为末时,读出前一个数据,实时触发一个flag,时钟采集这个flag,下一个
时钟采集到,给读片选切换,读地址清零,保留一次片选寄存器,即在用时钟赋值片选寄存器值,此时已经过去了两个时钟,也就是说读地址为1,读出的数据为另一块片子的0地址,即首地址,实时判断片选寄存器值来选择从哪块RAM中读数据。
这里两个芯片切换,写切换的是使能切换,读切换是哪个芯片读出数据切换。写地址增加使能和数据有效同步,读地址使能要等到写到完第一个片子才有效。读写的地址都是可以复用的,用写使能和读数据确定片子。
写RAM地址和数据同步,读RAM地址比数据快一个时钟,所以读切换时多一个时钟延迟。
64个words RAM做乒乓代码:
module pp64(
input rst,
input clk,
input[15:0] din,
input din_valid,
output[15:0] dout,
output reg dout_valid
);
parameter RAM_ADDR=64;
parameter RAM_WIDTH=6;
reg wr_cs;
wire wr_en_ram0;
wire wr_en_ram1;
reg[RAM_WIDTH-1:0] wr_addr;
wire wr_addr_end;
reg rd_cs;
reg rd_cs_dl;
reg rd_en;
reg[RAM_WIDTH-1:0] rd_addr;
wire rd_addr_end;
wire[15:0] data_ram0;
wire[15:0] data_ram1;
always @(negedge rst,posedge clk) begin
if(!rst)begin
wr_cs<=1'b0;
end
elseif(wr_addr_end) begin
wr_cs<=~wr_cs;
end
else begin
wr_cs<=wr_cs;
end
end
assign wr_en_ram0=(!wr_cs)?din_valid:1'b0;
assign wr_en_ram1=(wr_cs)?din_valid:1'b0;
always @(negedge rst,posedge clk) begin
if(!rst)begin
wr_addr<='d0;
end
elseif(din_valid) begin
if(wr_addr_end)begin
wr_addr<='d0;
end
elsebegin
wr_addr<=wr_addr+1'd1;
end
end
else begin
wr_addr<=wr_addr;
end
end
assign wr_addr_end=(wr_addr==RAM_ADDR-1)?1'b1:1'b0;
always @(negedge rst,posedge clk) begin
if(!rst)begin
rd_cs<=1'b0;
end
elseif(rd_addr_end) begin
rd_cs<=~rd_cs;
end
else begin
rd_cs<=rd_cs;
end
end
always @(negedge rst,posedge clk) begin
if(!rst)begin
rd_en<=1'b0;
end
elseif(wr_addr_end) begin
rd_en<=1'b1;
end
else begin
rd_en<=rd_en;
end
end
always @(negedge rst,posedge clk) begin
if(!rst)begin
rd_addr<='d0;
end
elseif(rd_en) begin
if(rd_addr_end)begin
rd_addr<='d0;
end
elsebegin
rd_addr<=rd_addr+1'd1;
end
end
end
assign rd_addr_end=(rd_addr==RAM_ADDR-1)?1'b1:1'b0;
always @(negedge rst,posedge clk) begin
if(!rst)begin
dout_valid<=1'b0;
end
else begin
dout_valid<=rd_en;
end
end
always @(negedge rst,posedge clk) begin
if(!rst)begin
rd_cs_dl<=1'b0;
end
else begin
rd_cs_dl<=rd_cs;
end
end
assign dout=(!rd_cs_dl)?data_ram0:data_ram1;
myram_ipcore myram0(
.clock(clk),
.data(din),
.rdaddress(rd_addr),
.rden(1'b1),
.wraddress(wr_addr),
.wren(wr_en_ram0),
.q(data_ram0)
);
myram_ipcore myram1(
.clock(clk),
.data(din),
.rdaddress(rd_addr),
.rden(1'b1),
.wraddress(wr_addr),
.wren(wr_en_ram1),
.q(data_ram1)
);
endmodule
再发一个FIFO 乒乓代码
用FIFO做乒乓,先了解FIFO IPCORE
myfifo_ipcore (
clock,
data,
rdreq,
wrreq,
empty,
full,
q,
usedw);
写使能wrreq和写入第一个数据data同步,当FIFO中最后一个内存被写入数据,full实时置高;读使能rdreq置高下一个时钟,读出第一个数q,FIFO中有第一个数后一个时钟empty置高。
写控制:
1. 初始复位,wr_cs置低,默认第一个片子;
2. 实时判断两个片子写满状态full,第一个片子写满,wr_cs置高,选通第二个片子,
第二个片子写满,wr_cs置低,选通第一个片子,并锁存状态直到下一次触发;
3. 写使能实时和wr_cs同步。
读控制:
1. 初始复位,总读使能置低,当第一个芯片满状态,置高并锁存;
2. 读使能和写片选实时同步,写第一个片子读第二个片子,写第二个片子就读第一个
片子,读使能要挂到总读使能上,就好像写使能要挂在总数据有效上一样;
3. 总读使能有效后一个时钟输出数据有效;
4. 总读使能,时钟采集满状态,当写满一个片子后,读数据片子取反。
module mypp64(
input rst,
input clk,
input[15:0] din,
input din_valid,
output[15:0] dout,
output reg dout_valid
);
wire wr_cs;
reg rd_cs;
wire wr_en_fifo0;
wire wr_en_fifo1;
wire rd_en_fifo0;
wire rd_en_fifo1;
wire full_fifo0;
wire full_fifo1;
wire rd_en;
wire[15:0] dout_fifo0;
wire[15:0] dout_fifo1;
assign wr_cs=(!rst)?1'b0:(full_fifo0)?1'b1:(full_fifo1)?1'b0:wr_cs;
assign rd_en=(!rst)?1'b0:(full_fifo0)?1'b1:rd_en;
assign wr_en_fifo0=(!wr_cs)?din_valid:1'b0;
assign wr_en_fifo1=(wr_cs)?din_valid:1'b0;
assign rd_en_fifo0=(!rst)?1'b0:(wr_cs)?rd_en:1'b0;
assign rd_en_fifo1=(!rst)?1'b0:(!wr_cs)?rd_en:1'b0;
always @(negedge rst,posedge clk) begin
if(!rst) begin
dout_valid<=1'b0;
end
else if(rd_en)begin
dout_valid<=1'b1;
end
else begin
dout_valid<=dout_valid;
end
end
always @(negedge rst,posedge clk) begin
if(!rst) begin
rd_cs<=1'b0;
end
else if(rd_en) begin
if(full_fifo0 || full_fifo1) begin
rd_cs<=~rd_cs;
end
else begin
rd_cs<=rd_cs;
end
end
else begin
rd_cs<=rd_cs;
end
end
assign dout=(rd_cs)?dout_fifo0:dout_fifo1;
myfifo_ipcore fifo0(
.clock(clk),
.data(din),
.rdreq(rd_en_fifo0),
.wrreq(wr_en_fifo0),
.empty(),
.full(full_fifo0),
.q(dout_fifo0),
.usedw()
);
myfifo_ipcore fifo1(
.clock(clk),
.data(din),
.rdreq(rd_en_fifo1),
.wrreq(wr_en_fifo1),
.empty(),
.full(full_fifo1),
.q(dout_fifo1),
.usedw()
);
endmodule
顶 不错哦 研究下
