由于是基于FPGA生成训练序列,因此需要先生成.coe文件,用于后续添加到RAM IP核中。
先打开matlab,新建脚本文件。
添加短训练序列生成代码,以及生成.coe文件的代码。
%% 短训练序列
%% Parameter
N_FFT = 64;
%% Short_preamble
S_k = sqrt(13/6)*[0,0,1+j,0,0,0,-1-j,0,0,0,1+j,0,0,0,-1-j,0,0,0,-1-j,0,0,0,1+j,0,0,0,0,0,0,0,-1-j,0,0,0,-1-j,0,0,0,1+j,0,0,0,1+j,0,0,0,1+j,0,0,0,1+j,0,0]; % [1x53]
virtual_subcarrier = zeros(1,N_FFT-length(S_k)); % [1x11] 虚拟子载波
Short_preamble_slot_Frequency = [virtual_subcarrier(1:6),S_k,virtual_subcarrier(7:11)]; % [1x64]
Short_preamble_slot_Time = ifft(ifftshift(Short_preamble_slot_Frequency)); % [1x64]
Short_preamble = repmat(Short_preamble_slot_Time(1:16),1,10); % [1x160]
% 实部和虚部分开
Short_preamble_real = real(Short_preamble_slot_Time);
Short_preamble_image = imag(Short_preamble_slot_Time);
% 定点化:生成16位二进制补码字符串(列向量)
Short_preamble_real_bin = bin(fi(Short_preamble_real,1,16,13).');
Short_preamble_image_bin = bin(fi(Short_preamble_image,1,16,13).');
% 你的核心拼接逻辑:虚部16位 + 虚部16位 → 32位二进制串
Short = [(Short_preamble_image_bin), (Short_preamble_real_bin)]; % 64行×32列的字符数组
%%截取Short的前16行,传入函数
gen_coe_binary('short_preamble.coe', Short(1:16, :)); % 只取前16行
%% COE生成函数
function gen_coe_binary(fn, bin_char_array)
fid = fopen(fn, 'w');
if fid == -1
error('无法打开文件!请检查路径是否含中文/特殊字符');
end
% COE文件头:二进制基数(radix=2)
fprintf(fid, 'memory_initialization_radix=2;\n');
fprintf(fid, 'memory_initialization_vector=\n');
% 遍历截取后的行
[n_rows, ~] = size(bin_char_array); % 此时n_rows=16
for i = 1:n_rows
bin_str = bin_char_array(i, :); % 提取第i行32位二进制串
if i == n_rows
fprintf(fid, '%s;\n', bin_str);
else
fprintf(fid, '%s,\n', bin_str);
end
end
fclose(fid);
disp(['COE文件已生成:', fullfile(pwd, fn)]);
end
运行后,生成short_preamble.coe文件。如下所示。

添加长训练序列生成代码,以及生成.coe文件的代码。
%% Parameter
N_FFT = 64;
%% Long_preamble premble:前导码用来同步发射机和接受机,以维系两者之间的定时关系
L_k = [1,1,-1,-1,1,1,-1,1,-1,1,1,1,1,1,1,-1,-1,1,1,-1,1,-1,1,1,1,1,0,1,-1,-1,1,1,-1,1,-1,1,-1,-1,-1,-1,-1,1,1,-1,-1,1,-1,1,-1,1,1,1,1]; % [1x53]
virtual_subcarrier = zeros(1,N_FFT-length(L_k)); % [1x11]
Long_preamble_slot_Frequency = [virtual_subcarrier(1:6),L_k,virtual_subcarrier(7:11)]; % [1x64]
Long_preamble_slot_Time = ifft(ifftshift(Long_preamble_slot_Frequency)); % [1x64]
Long_preamble = [Long_preamble_slot_Time(33:64),Long_preamble_slot_Time,Long_preamble_slot_Time]; % [1x160]
%实部和虚部分开
Long_preamble_real = real(Long_preamble_slot_Time);
Long_preamble_image = imag(Long_preamble_slot_Time);
%定点
Long_preamble_real_bin =bin(fi(Long_preamble_real,1,16,13).');
Long_preamble_image_bin =bin(fi(Long_preamble_image,1,16,13).');
long=[(Long_preamble_image_bin),(Long_preamble_real_bin)];
%% 调用函数生成COE(默认生成完整64行)
gen_coe_binary('long_preamble.coe', long);
%% COE生成函数(与短训练序列共用同一逻辑,确保格式统一)
function gen_coe_binary(fn, bin_char_array)
% 打开文件(写入模式,不存在则创建)
fid = fopen(fn, 'w');
if fid == -1
error('无法打开文件!请检查路径是否含中文/特殊字符');
end
% COE文件头:二进制基数(radix=2),与数据格式匹配
fprintf(fid, 'memory_initialization_radix=2;\n');
fprintf(fid, 'memory_initialization_vector=\n');
% 遍历所有数据行(默认64行,如需前16行可修改下面的索引)
[n_rows, ~] = size(bin_char_array);
for i = 1:n_rows
% 提取第i行的32位二进制字符串(不修改任何字符)
bin_str = bin_char_array(i, :);
% 遵循COE语法:最后一行用分号,其余用逗号
if i == n_rows
fprintf(fid, '%s;\n', bin_str);
else
fprintf(fid, '%s,\n', bin_str);
end
end
fclose(fid);
disp(['长训练序列COE文件已生成:', fullfile(pwd, fn)]);
end
生成的长训练序列如下所示。
memory_initialization_radix=2;
memory_initialization_vector=
00000000000000000000010100000000,
11111100001001101111111111010110,
11111100011100010000000101000110,
00000010101001100000001100011001,
00000000111001000000000010101101,
11111101001100100000000111101010,
11111110001111001111110001010001,
11111100100110101111111011000110,
11111111001011000000001100011111,
00000000001000010000000110110101,
11111100010100100000000000001000,
11111110011111001111101110011111,
11111110001000010000000011001001,
11111111100001100000000111100001,
00000101001001001111111101001000,
11111111110111100000001111010001,
11111110000000000000001000000000,
00000011001001100000000100101110,
00000001010000101111111000101011,
00000010000101101111101111001101,
00000010111101010000001010100010,
00000000011101000000001000111010,
00000010100110101111111000010010,
11111111010011011111111000110010,
11111011001011001111111011100001,
11111111011110001111110000011010,
11111111010110001111101111101101,
11111101101000010000001001100111,
00000001101110011111111111101001,
00000011101011111111110100001111,
00000011011000110000001011101111,
00000011001000000000000001100101,
00000000000000001111101100000000,
11111100111000000000000001100101,
11111100100111010000001011101111,
11111100010100011111110100001111,
11111110010001111111111111101001,
00000010010111110000001001100111,
00000000101010001111101111101101,
00000000100010001111110000011010,
00000100110101001111111011100001,
00000000101100111111111000110010,
11111101011001101111111000010010,
11111111100011000000001000111010,
11111101000010110000001010100010,
11111101111010101111101111001101,
11111110101111101111111000101011,
11111100110110100000000100101110,
00000010000000000000001000000000,
00000000001000100000001111010001,
11111010110111001111111101001000,
00000000011110100000000111100001,
00000001110111110000000011001001,
00000001100001001111101110011111,
00000011101011100000000000001000,
11111111110111110000000110110101,
00000000110101000000001100011111,
00000011011001101111111011000110,
00000001110001001111110001010001,
00000010110011100000000111101010,
11111111000111000000000010101101,
11111101010110100000001100011001,
00000011100011110000000101000110,
00000011110110101111111111010110;
注意代码里的memory_initialization_radix=2参数不要修改,表示2进制。用于RAM 模块编译时识别。
建好vivado工程后,把生成好的两个.coe文件,添加到vivado工程中的 .srcs/sources_1目录下,便于软件编译时扫描。

打开vivado工程。点击IP Catalog

搜索bram。

配置短训练序列的RAM ip核。



点击Browse添加.coe文件。

配置完后,点击OK,等待generate完成后,再添加一个ram,配置长训练序列的ip核。下面再对长训练序列的ip核进行配置。


一样等待generate完成后,后面开始编写读取ram的代码。
先读取短训练序列,新建.v文件,对short_preamble.v进行编写
因为需要用例化的RAM模块,读取里面的训练序列,打开生成的.veo文件

添加到short_preamble.v中
`timescale 1ns / 1ps
module preamble_short(
input clk,
input rst_n,
output [31:0] short_data,
output reg short_predone_flag,
output reg short_en
);
reg [3:0] short_addr;
reg [3:0] short_cnt;
reg [17:0] delay_cnt;
reg start_flag;
reg short_en_d;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
delay_cnt <= 18'd0;
start_flag <= 1'b0;
end else if(delay_cnt == 18'd250_000) begin
delay_cnt <= 18'd0;
start_flag <= 1'b0;
end else if(delay_cnt == 18'd250_000 - 1) begin
delay_cnt <= delay_cnt + 1'b1;
start_flag <= 1'b1;
end else begin
delay_cnt <= delay_cnt + 1'b1;
start_flag <= 1'b0;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
short_en_d <= 1'b0;
end else if(start_flag) begin
short_en_d <= 1'b1;
end else if(short_predone_flag) begin
short_en_d <= 1'b0;
end else begin
short_en_d <= short_en_d;
short_en <= short_en_d;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
short_addr <= 4'd0;
short_cnt <= 4'd0;
short_predone_flag <= 1'b0;
end else if(short_en) begin
short_predone_flag <= 1'b0;
if(short_addr == 4'd15) begin
short_addr <= 4'd0;
short_cnt <= short_cnt + 1'b1;
end else begin
short_addr <= short_addr + 1'b1;
end
if((short_cnt == 4'd9) && (short_addr == 4'd15)) begin
short_predone_flag <= 1'b1;
end
end
else begin
short_addr <= 4'd0;
short_cnt <= 4'd0;
short_predone_flag <= 1'b0;
end
end
short_preamble short_preamble_u(
.clka(clk),
.ena(short_en),
.addra(short_addr),
.douta(short_data)
);
endmodule

再新建一个.v文件,读取长训练序列,对long_preamble.v进行编写
`timescale 1ns / 1ps
module preamble_long(
input clk,
input rst_n,
input short_predone_flag,
output [31:0] long_data,
output reg long_en,
output reg long_done
);
reg [5:0] long_addr;
reg [1:0] long_done_flag;
reg [1:0] long_cnt;
reg short_predone_flag_d1;
reg [5:0] long_addr_d1;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
short_predone_flag_d1 <= 1'b0;
end else begin
short_predone_flag_d1 <= short_predone_flag;
end
end
wire long_start = short_predone_flag & ~short_predone_flag_d1;
wire long_rom_en = long_en || short_predone_flag;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
long_en <= 1'b0;
end else if(long_start) begin
long_en <= 1'b1;
end else if(long_done) begin
long_en <= 1'b0;
end else begin
long_en <= long_en;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
long_addr <= 6'd0;
long_cnt <= 2'd0;
end
else if(long_start) begin
long_addr <= 6'd33;
long_cnt <= 2'd0;
end
else if(long_en == 1'b1)begin
case(long_cnt)
2'd0:
if(long_addr == 6'd63)begin
long_addr <= 6'd0;
long_cnt <= long_cnt+1'b1;
end
else begin
long_addr <= long_addr+1'd1;
long_cnt <= long_cnt;
end
2'd1:
if(long_addr == 6'd63)begin
long_addr <= 6'd0;
long_cnt <= long_cnt+1'b1;
end
else begin
long_addr <= long_addr+1'd1;
long_cnt <= long_cnt;
end
2'd2:
if(long_addr == 6'd63)begin
long_addr <= 6'd63;
end
else begin
long_addr <= long_addr+1'd1;
long_cnt <= 2'd2;
end
default:begin
long_addr <= 6'd0;
long_cnt <= 2'd0;
end
endcase
end
else begin
long_addr <= 6'd32;
long_cnt <= 2'd0;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
long_done_flag<=2'b00;
end
else if(long_addr == 6'd61 && long_cnt==2'd2 && long_done_flag == 2'b00 && long_en == 1'b1)begin
long_done_flag<=2'b10;
end
else if(long_done_flag==2'b10)begin
long_done_flag<=2'b11;
end
else begin
long_done_flag<=2'b00;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
long_done<=1'b0;
//long_done_pulse_d1 <= 1'b0;
end
else begin
long_done<= (long_done_flag==2'b11);
//long_done_pulse_d1 <= (long_done_flag==2'b11);
//long_done <= long_done_pulse_d1;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
long_addr_d1 <= 6'd0;
else
long_addr_d1 <= long_addr;
end
long_preamble u_long_preamble(
.clka(clk),
.ena(long_rom_en),
.addra(long_addr),
.douta(long_data)
);
endmodule
新建一个preamble.v文件,用于对短训练序列和长训练序列进行衔接。
`timescale 1ns / 1ps
module preamble(
input clk,
input rst_n,
output reg [31:0] train_data,
output reg train_data_valid,
output reg train_data_done
);
wire [31:0] short_data;
wire [31:0] long_data;
wire short_predone_flag;
wire short_en;
wire long_en;
wire long_done;
reg short_en_d1;
reg short_en_d2;
reg long_en_d1;
reg long_done_d1;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
short_en_d1 <= 1'b0;
short_en_d2 <= 1'b0;
long_en_d1 <= 1'b0;
long_done_d1 <= 1'b0;
end else begin
short_en_d1 <= short_en;
short_en_d2 <= short_en_d1;
long_en_d1 <= long_en;
long_done_d1 <= long_done;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
train_data <= 32'b0;
end else if(long_en_d1) begin
train_data <= long_data;
end else if(short_en_d2) begin
train_data <= short_data;
end else begin
train_data <= 32'b0;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
train_data_valid <= 1'b0;
end
else if(short_en_d2 || long_en_d1) begin
train_data_valid <= 1'b1;
end
else begin
train_data_valid <= 1'b0;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
train_data_done <= 1'b0;
end
else begin
train_data_done <= long_done_d1;
end
end
preamble_short u_preamble_short(
.clk (clk ),
.rst_n (rst_n ),
.short_data (short_data ),
.short_predone_flag(short_predone_flag),
.short_en (short_en )
);
preamble_long u_preamble_long(
.clk (clk ),
.rst_n (rst_n ),
.short_predone_flag(short_predone_flag),
.long_data (long_data ),
.long_en (long_en ),
.long_done (long_done )
);
ila_0 ila_preamble(
.clk(clk), // input wire clk
.probe0(train_data), // input wire [31:0] probe0
.probe1(train_data_valid), // input wire [0:0] probe1
.probe2(train_data_done), // input wire [0:0] probe2
.probe3(short_en), // input wire [0:0] probe3
.probe4(long_en), // input wire [0:0] probe4
.probe5(short_predone_flag), // input wire [0:0] probe5
.probe6(short_data), // input wire [31:0] probe6
.probe7(long_data), // input wire [31:0] probe7
.probe8(long_done) // input wire [3:0] probe8
);
endmodule
在preamble.v中添加一个ila用于查看波形是否正确。
文件都进行保存后,点击生成比特流
然后在vivado中进行烧录。



设置触发条件后,点击开始进行捕捉。可以查看到波形触发

下面是长短训练衔接部分。

训练序列的末尾。

最终训练序列生成成功,短训练序列连续读取10次,共160个。长训练序列先读取后32个,再读取两次64个,共160个。总共320个训练序列。

1921

被折叠的 条评论
为什么被折叠?



