在参加集创赛过程中,发现国产的FPGA的资料文件非常稀缺,很多东西废了千辛万苦自己把代码手撕出来,为了以防自己会忘了,决定记录下一些自认为比较重要的东西,也方便后来人可以查看。

模块输入输出

这里的输入是16位的数字音频信号,当且仅当i_en有效时候,因此后续需要逻辑块(always)对其赋值,输入配置如下:

  • 输入时钟信号
  • 输入系统复位信号(0有效)
  • 输入音频信号有效信号(这个自行更改)
  • 输入音频信号16位
  • 读出乒乓操作后的数据

状态机

  • IDLE:初始状态,在不工作或复位时就让状态机置为初始状态。
  • WRAM1:写RAM1状态。该状态我们开始往RAM1中写入数据,此时由于RAM2中并没有写入数据,所以我们不用对RAM2进行读取。
  • WRAM2_RRAM1:写RAM2读RAM1状态,当第一包数据写入完毕之后,马上跳到该状态,将第二包数据写入到RAM2中的同时读出RAM1中的写入的第一包数据。
  • WRAM1_RRAM2:写RAM1读RAM2状态。在该状态下我们开始向RAM1中写入第三包数据,此时第三包数据会把第一包数据覆盖,而我们的第一包数据已经读取出来了,并不会使数据丢失。在往RAM1中写入第三包数据的同时,我们读出RAM2中的第二包数据,当读写完成之后,跳回WRAM2_RRAM1状态开始 下一包的数据写入以及读取,如此循环我们就能无缝地将连续的输入数据读取出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//状态跳转
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin //空闲
if (wea == 1'b1) begin
state <= WRAM1;
end
else begin
state <= IDLE;
end
end
WRAM1:begin //写R1
if ((wea == 1'b0)&&(web == 1'b1)) begin
state <= WRAM2_RRAM1;
end
else begin
state <= WRAM1;
end
end
WRAM2_RRAM1:begin //读R1,写R2
if ((wea == 1'b1)&&(web == 1'b0))begin
state <= WRAM1_RRAM2;
end
else begin
state <= WRAM2_RRAM1;
end
end
WRAM1_RRAM2:begin //写R1,读R2
if ((wea == 1'b0)&&(web == 1'b1))begin
state <= WRAM2_RRAM1;
end
else begin
state <= WRAM1_RRAM2;
end
end
default:begin
state <= IDLE;
end
endcase
end
end

计数器

wea和web是ram的使能,有的ram有读使能和写使能,这里做了简化,使能为1就写,使能为0就读。

1
2
3
//组合逻辑
assign wea = ((ram_cnt <= 9'd255)&&(ena == 1'b1)) ? 1 : 0;
assign web = ~wea;

上述代码搭配计数器就可以实现一半时序为读使能一半时序为写使能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//读写状态计数器
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
ram_cnt <= 9'd0;
end
else if(ram_cnt < 9'd511)begin
ram_cnt <= ram_cnt + 1'b1;
end
else begin
ram_cnt <= 9'd0;
end
end
//读写地址计数器
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
ram_addr <= 9'd0;
end
else if(ram_addr < 9'd255)begin
ram_addr <= ram_addr + 1'b1;
end
else begin
ram_addr <= 9'd0;
end
end

RAM配置

既然是乒乓操作,必然需要两片空间可以交换读取的,这里选取了RAM,配置如下:

  • 写数据16位(即[15:0])
  • 地址9位(即[8:0])
  • 输入时钟信号i_clk
  • 启动信号wea or web(这里配置为!rst,即不复位有效,rst是0的时候系统复位)
  • 读数据信号16位(即[15:0])

整体实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
module ram_in_out(
input i_clk /*synthesis PAP_MARK_DEBUG="ture"*/,
inout i_rst,
input i_en /*synthesis PAP_MARK_DEBUG="1"*/,//左声道或右声道有效
input signed [15:0] i_data_x/*synthesis PAP_MARK_DEBUG="1"*/,//只考虑左声道或右声道
output [15:0] o_data_y /*synthesis PAP_MARK_DEBUG="1"*/
);


parameter HHWS = 10'b1_0000_0000;//256没用到
wire wea/*synthesis PAP_MARK_DEBUG="1"*/;
wire web/*synthesis PAP_MARK_DEBUG="1"*/;

/*************************寄存器****************************/
reg signed [15:0] r_outdata_y;//结果寄存器
//读写状态计数器
reg [8:0] ram_cnt/*synthesis PAP_MARK_DEBUG="1"*/;
reg [8:0] ram_addr/*synthesis PAP_MARK_DEBUG="1"*/;

reg [3:0] state/*synthesis PAP_MARK_DEBUG="1"*/;
reg [15:0] r_data;
//aram和bram寄存器定义
reg [8:0] addra/*synthesis PAP_MARK_DEBUG="1"*/;
reg [15:0] dina/*synthesis PAP_MARK_DEBUG="1"*/;
wire [15:0] douta/*synthesis PAP_MARK_DEBUG="1"*/;
reg [8:0] addrb/*synthesis PAP_MARK_DEBUG="1"*/;
reg [15:0] dinb/*synthesis PAP_MARK_DEBUG="1"*/;
wire [15:0] doutb/*synthesis PAP_MARK_DEBUG="1"*/;

//״̬
parameter IDLE = 4'b0001; //空闲
parameter WRAM1 = 4'b0010; //写R1
parameter WRAM2_RRAM1 = 4'b0100; //读R1写R2
parameter WRAM1_RRAM2 = 4'b1000; //写R1读R2

/*************************组合逻辑**************************/
//R1读写切换
assign wea = ((ram_cnt <= 9'd255)&&(ena == 1'b1)) ? 1 : 0;
//R2读写切换
assign web = ~wea;
assign o_data_y = r_outdata_y;
assign ena = i_rst;
/*************************状态机****************************/
//状态跳转
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin //空闲
if (wea == 1'b1) begin
state <= WRAM1;
end
else begin
state <= IDLE;
end
end
WRAM1:begin //写R1
if ((wea == 1'b0)&&(web == 1'b1)) begin
state <= WRAM2_RRAM1;
end
else begin
state <= WRAM1;
end
end
WRAM2_RRAM1:begin //读R1,写R2
if ((wea == 1'b1)&&(web == 1'b0))begin
state <= WRAM1_RRAM2;
end
else begin
state <= WRAM2_RRAM1;
end
end
WRAM1_RRAM2:begin //写R1,读R2
if ((wea == 1'b0)&&(web == 1'b1))begin
state <= WRAM2_RRAM1;
end
else begin
state <= WRAM1_RRAM2;
end
end
default:begin
state <= IDLE;
end
endcase
end
end

/*************************时序逻辑**************************/

always @(posedge i_clk or negedge i_rst) begin
if(!i_rst)
r_data <= 'd0;
else if(i_en)
r_data <= i_data_x;
else
r_data <= r_data;
end


//读写状态计数器
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
ram_cnt <= 9'd0;
end
else if(ram_cnt < 9'd511)begin
ram_cnt <= ram_cnt + 1'b1;
end
else begin
ram_cnt <= 9'd0;
end
end
//读写地址计数器
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
ram_addr <= 9'd0;
end
else if(ram_addr < 9'd255)begin
ram_addr <= ram_addr + 1'b1;
end
else begin
ram_addr <= 9'd0;
end
end

//读写数据
always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
//state <= IDLE;
end
else begin
case (state)
IDLE:begin
addra <= 9'b0;
dina <= 16'b0;
addrb <= 9'b0;
dinb <= 16'b0;
end
WRAM1:begin
addra <= ram_addr;
dina <= r_data;
end
WRAM2_RRAM1:begin
addra <= ram_addr;
addrb <= ram_addr;
dinb <= r_data;
end
WRAM1_RRAM2:begin
addra <= ram_addr;
addrb <= ram_addr;
dina <= r_data;
end
default:begin
// addra <= addra;
// dina <= dina;
// addrb <= addrb;
// dinb <= dinb;
end
endcase
end
end

ram_init a_ram (
.wr_data(dina), // input [15:0]
.addr(addra), // input [8:0]
.clk(i_clk), // input
.wr_en(wea), // input
.rst(i_rst), // input
.rd_data(douta) // output [15:0]
);

ram_init b_ram (
.wr_data(dinb), // input [15:0]
.addr(addrb), // input [8:0]
.clk(i_clk), // input
.wr_en(web), // input
.rst(i_rst), // input
.rd_data(doutb) // output [15:0]
);

always @(posedge i_clk or negedge i_rst) begin
if (!i_rst) begin
r_outdata_y <='d0;
end
else if (web) begin
r_outdata_y <= douta;
end
else if (wea) begin
r_outdata_y <= doutb;
end
else
r_outdata_y <= r_outdata_y;
end
endmodule

(新手第一次写博客,如有错误和不好的地方,请多担待,另外对程序有问题请提出噢!)