基於FPGA的映射調製實現

項目簡述

在通信過程中,經常能碰見調製解調,這裏將講解一種映射調製的方法,並且給出相應的代碼供大家學習。一般調製位於無線通信中比較靠後的未知,在交織之後。這裏的映射調製並沒有介入載波,只是進行了相應的星座圖映射,至於最後載波的加入,將在後面的博客中學習。

數學建模

首先我們常見的QPSK與16-QAM的星座圖如下:
在這裏插入圖片描述
由星座圖可以得到編碼之後的如下信息:
QPSK編碼對應座標軸大小
在這裏插入圖片描述
在這裏插入圖片描述
16-QAM編碼對應座標軸大小
在這裏插入圖片描述
在這裏插入圖片描述

星座圖映射的對象爲位交織輸出 u=(u0,u1,...,uNldpc2,uNldpc1)u=(u_0,u_1,...,u_{N_{ldpc}-2},u_{N_{ldpc}-1}) , Nldpc=16200N_{ldpc}=16200主要是因爲LDPC編碼之後每包的長度就是16200,星座圖映射分爲兩步:
(1)解複用,將uu 分解爲 NsubstreamsN_{substreams} 個子流
(2)將 NsubstreamsN_{substreams} 個子流根據調製方式映射到對應星座圖。

那麼對於不同的調製方式, $N_{substreams}的值如下:

Modulation Number of sub-streams NsubstreamsN_{substreams}
QPSK 2
16-QAM 8

首先我們要完成解複用,其實說白了解複用就是一個串並變換。

在這裏插入圖片描述
解複用模塊將輸入vdiv_{di} 映射到輸出bi,jb_{i,j},其中ii爲第ii個子流,j=di div Nsubstreamsj=di \ div\ N_{substreams}。其中對於QPSK調製,i與di mod Nsubstreamsdi \ mod\ N_{substreams}的值一樣,對於16-QAM則進行了相應的變換,如下表:

Modulation format QPSK
di mod Nsubstreamsdi \ mod\ N_{substreams} 0 1
e 0 1
Modulation format 16-QAM
di mod Nsubstreamsdi \ mod\ N_{substreams} 0 1 2 3 4 5 6 7
e 7 1 4 2 5 3 6 0

進行完解複用之後就應該進行相應的映射操作,將會將上面產生的NsubstreamsN_{substreams}個並行通道映射到相應的σ\sigma個碼元,映射的關係如下表:

Modulation NsubstreamsN_{substreams} σ\sigma
QPSK 2 2
16-QAM 8 4

對於 QPSK 調製, Nsubstreams 個子流每次組成一個調製符號,而對於 16-QAM,NsubstreamsN_{substreams} 個子流每次組成兩個調製符號,前半個子流組成一個調製符號,後半個子流組成一個調製符號。具體公式如下:
定義調製符號:ydo=[y0,do,...,yσ1,do]y_{do}=[y_{0,do},...,y_{\sigma-1,do}]
對於QPSK調製:[y0,do,...,yσ1,do]=[b0,do,...,bNsubstreams1,do][y_{0,do},...,y_{\sigma-1,do}]=[b_{0,do},...,b_{N_{substreams}-1,do}]
對於16-QAM調製:[y0,2do,...,yσ1,2do]=[b0,do,...,bNsubstreams/21,do][y_{0,2do},...,y_{\sigma-1,2do}]=[b_{0,do},...,b_{N_{substreams}/2-1,do}]
[y0,2do+1,...,yσ1,2do+1]=[bNsubstreams/2,do,...,bNsubstreams1,do][y_{0,2do+1},...,y_{\sigma-1,2do+1}]=[b_{N_{substreams}/2,do},...,b_{N_{substreams}-1,do}]
然後就完成了相應的映射操作。也就將原來的碼映射到了星座表中的座標上。

MATLAB仿真

上面我們已經講解了映射調製的星座圖對應關係,但是相信大家還都不是特別明白,接下來給出相應的MATLAB代碼供大家學習,學習的時候需要結合數學模型與MATLAB仿真相互學習。

sim_options = struct(...
    'CONSTELLATION', '16-QAM' ...
);
fid1 = fopen('mapper_in.txt','r');
DataIn = fscanf(fid1,'%d');
%------------------------------------------------------------------------------
%------------------------------------------------------------------------------
switch sim_options.CONSTELLATION
    case 'QPSK'
        V        = 2;                       % Bits per cell
    case '16-QAM'
        V        = 4;
    otherwise, error('sim_options UNKNOWN CONSTELLATION');
end
%------------------------------------------------------------------------------
% Parameters Definition
%------------------------------------------------------------------------------
CONSTEL  = sim_options.CONSTELLATION;
%------------------------------------------------------------------------------
% Procedure
%------------------------------------------------------------------------------
%bits to cells
data = DataIn';
streams = V * 2;
switch CONSTEL
  case 'QPSK'
    mapping = [0 1 2 3] + 1;
  case '16-QAM' 
      mapping = [7 1 4 2 5 3 6 0] + 1;
end

numBits  = length(data);
numCells = floor(numBits/streams);
numBits  = numCells*streams;
data = data(1:numBits);
%make streams
cells = reshape(data', streams, size(data,2)/streams)';
%swap this to agree with spec, above line maps the wrong way
cells(:, mapping) = cells;


cells = reshape(cells', [] , 1);
cells = reshape(cells, V , [])';

if ~isempty(cells)
    DataOut = bi2de(cells,'left-msb');
else
    DataOut = [];
end
save DataOut.mat DataOut

上面的代碼主要是源於電子發燒友學院,只是博主爲了方便理解,進行了一小部分的更改。需要的同學可以關注上面的課程信息。閱讀完相應的MATLAB代碼,再觀察前面的數學模型,會發現映射調製非常簡單。

設置調製方式:
在這裏插入圖片描述
找到輸入數據:
在這裏插入圖片描述
設置映射方式:
在這裏插入圖片描述
解複用:
在這裏插入圖片描述
映射到星座圖:
在這裏插入圖片描述

FPGA代碼

上面我們已經詳細介紹了映射調製的原理與MATLAB代碼,這裏將給出相應的MATLAB代碼。代碼非常簡單,這裏也就不對說了。

FPGA映射的代碼

tx_Mapper模塊:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : tx_Mapper.v
// Create Time  : 2020-05-31 11:23:32
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

 
 module tx_Mapper(
	input  	     	      	clk 						,
	input  	     	      	rst_n 						,
	
	input  	     	      	s_config_tvalid 			,
	input  	     	      	s_config_tdata 				,
	input  	     	      	s_data_tvalid				,
	input  	     	      	s_data_tdata				,
	output 	reg  	      	s_data_tready				,
	input  	     	      	s_data_tlast				,
						
	output 	reg  	      	m_data_tvalid				,
	output 	reg  	[ 3:0]	m_data_tdata				,
	input  	     	      	m_data_tready				,
	output 	reg  	      	m_data_tlast					 		 
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/

reg        					mode_type					;
reg 				[12:0] 	Ncell 						;

reg 				[ 1:0]  QPSK_cnt					;
reg 				[ 3:0]  QPSK_data					;
reg 				        map_ena						;
reg 				[ 7:0]  QPSK_mod_data				;
reg        					QPSK_mod_valid				;


reg 				[ 2:0]  QAM_cnt 					;
reg 				[ 7:0]  QAM_data					;
reg 				        map_enb						;
reg 				[ 7:0]  QAM_mod_data				;
reg 				        QAM_mod_valid				;

reg 				       	map_wea 					;
reg 				[11:0] 	map_addra 					;
reg 				[ 7:0]  map_dina 					;
reg 				[12:0] 	map_addrb 					;
wire				[ 3:0]  map_doutb 					;

reg        					data_ordy					;
reg 				[12:0] 	m_cnt						;
reg        					s_data_tvalid_reg 			;
wire       					start_data_invld 		 	;	
	
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/

assign start_data_invld 	= 			~s_data_tvalid_reg && s_data_tvalid;       
	
//====================================================================================
// configure parameter
//====================================================================================
always@(posedge clk)
	if(rst_n == 1'b0)
		mode_type 			<= 			0;
	else if(s_config_tvalid == 1'b1)
		mode_type 			<= 			s_config_tdata; ///mode_type = 0,QPSK ; mode_type = 1 ,16QAM;

	 
always@(*)
    case(mode_type)
		1'b0	: 		Ncell 		= 		13'd8100;
		1'b1	: 		Ncell 		= 		13'd4050;
		default	: 		Ncell 		= 		13'd0;
	endcase
	
always@(posedge clk)
	if(mode_type == 1'b0 && s_data_tvalid == 1'b1)
		QPSK_cnt 		<=      	QPSK_cnt + 1'b1;
	else
		QPSK_cnt 		<=      	0;
	   
always@(posedge clk)
	if(rst_n == 1'b0)
		QPSK_data 		<=      	0;
	else case(QPSK_cnt)	
		2'b00	: 	QPSK_data[0] 	<=      s_data_tdata;
		2'b01	: 	QPSK_data[1] 	<=      s_data_tdata;
		2'b10	: 	QPSK_data[2] 	<=      s_data_tdata;
		2'b11	: 	QPSK_data[3] 	<=      s_data_tdata;
		default	: 	QPSK_data 		<= 		QPSK_data;
	endcase
	
always@(posedge clk)
	if(QPSK_cnt == 2'b11)
		map_ena 		<=			1'b1;
	else
		map_ena 		<=      	1'b0;
	 
always@(posedge clk)
	if(map_ena)begin
		QPSK_mod_data 	<=      	{2'b00,QPSK_data[2],QPSK_data[3],2'b00,QPSK_data[0],QPSK_data[1]};
		QPSK_mod_valid 	<=      	1'b1;
	end else begin
		QPSK_mod_data 	<=      	0;
		QPSK_mod_valid 	<=      	0;
	end
	
//16QAM
always@(posedge clk)
	if(mode_type == 1'b1 && s_data_tvalid)
		QAM_cnt 		<=      	QAM_cnt + 1'b1;
	else
		QAM_cnt 		<=      	0;
	   
always@(posedge clk)
	if(rst_n == 1'b0)
		QAM_data <=      0;
	else case(QAM_cnt)
		3'b000	: 	QAM_data[0] 	<=      s_data_tdata;
		3'b001	: 	QAM_data[1] 	<=      s_data_tdata;
		3'b010	: 	QAM_data[2] 	<=      s_data_tdata;
		3'b011	: 	QAM_data[3] 	<=      s_data_tdata;
		3'b100	: 	QAM_data[4] 	<=      s_data_tdata;
		3'b101	: 	QAM_data[5] 	<=      s_data_tdata;
		3'b110	: 	QAM_data[6] 	<=      s_data_tdata;
		3'b111	: 	QAM_data[7] 	<=      s_data_tdata;
		default	: 	QAM_data 		<= 		QAM_data;
	endcase
    
always@(posedge clk)
	if(QAM_cnt == 3'b111)
		map_enb 		<=  		1'b1;
	else
		map_enb 		<=      	1'b0;
	 
always@(posedge clk)
	if(map_enb)begin
		QAM_mod_data 	<=      	{QAM_data[0],QAM_data[6],QAM_data[4],QAM_data[2],QAM_data[5],QAM_data[3],QAM_data[1],QAM_data[7]};
		QAM_mod_valid 	<=      	1'b1;
	end else begin
		QAM_mod_data 	<=      	0;
		QAM_mod_valid 	<=      	0;
	end	
		
always@(posedge clk)
	map_wea 			<=      QAM_mod_valid || QPSK_mod_valid;
	
always@(posedge clk)
	if(mode_type == 0)
		map_dina 		<=	  		QPSK_mod_data ;
	else
		map_dina 		<=			QAM_mod_data;
	 
always@(posedge clk)
	if(rst_n == 1'b0)
		map_addra 		<=      	0;
	else if(mode_type == 0 && map_addra == 12'd4050)
		map_addra 		<=      	0;
	else if(mode_type == 1 && map_addra == 12'd2025)
		map_addra 		<=      	0;
	else if(map_wea == 1'b1)
		map_addra 		<=      	map_addra + 1'b1;
	
	
always@(posedge clk)
	if(map_addra > 12'd3100 && mode_type == 0)
		map_addrb 		<=      	map_addrb + 1'b1;
	else if(map_addra > 12'd1800 && mode_type == 1)
		map_addrb 		<=      	map_addrb + 1'b1;
	else if(map_addrb < (Ncell-1) && map_addrb > 0)
		map_addrb 		<=      	map_addrb + 1'b1;
	else 
		map_addrb 		<=      	0;
	  
//====================================================================================
// Output
//====================================================================================
	  
always@(posedge clk)
	if(mode_type)
		m_data_tdata 	<=      	{map_doutb[0],map_doutb[1],map_doutb[2],map_doutb[3]};
	else 
		m_data_tdata 	<=      	map_doutb;
	  
always@(posedge clk)
	if(~rst_n)
		m_data_tvalid 	<=      	0;
	else if(map_addrb > 0)
		m_data_tvalid 	<=      	1;
	else if(m_cnt == (Ncell -1))
		m_data_tvalid 	<=      	0;
	
always@(posedge clk)
	if(m_data_tvalid)
		m_cnt 			<=      	m_cnt + 1'b1;
	else
		m_cnt 			<=      	0;  
	  
always@(posedge clk)
	if(m_cnt == (Ncell-2))
		m_data_tlast 	<=      	1'b1;
	else
		m_data_tlast 	<=      	1'b0;
	  
	  
//====================================================================================
// m_data_tready && s_data_tready
//====================================================================================

	
always@(posedge clk)
	s_data_tvalid_reg 	<=      	s_data_tvalid;

always@(posedge clk)
	if(rst_n == 1'b0)
		data_ordy 		<=      	1'b0;
	else if(s_config_tvalid == 1'b1)
		data_ordy 		<=      	1'b1;
	else if(start_data_invld == 1'b1)
		data_ordy 		<=      	1'b0;
	else if(m_data_tlast == 1'b1)
		data_ordy 		<=      	1'b1;
		 
always@(posedge clk)
	s_data_tready 		<=      	data_ordy && m_data_tready;
	 

map_ram map_ram(
	.clka 				(clk				),    // input wire clka
	.wea				(map_wea			),      // input wire [0 : 0] wea
	.addra				(map_addra			),  // input wire [11 : 0] addra
	.dina				(map_dina			),    // input wire [7 : 0] dina
	.clkb				(clk				),    // input wire clkb
	.addrb				(map_addrb			),  // input wire [12 : 0] addrb
	.doutb				(map_doutb			)  // output wire [3 : 0] doutb
);
	 
endmodule

這部分代碼也是主要參考了電子發燒友學院中的課程。代碼非常簡單,大家自己閱讀學習即可,這裏不再詳細說明。

FPGA的映射測試代碼

測試代碼

`timescale 1ns / 1ps


module tb_tx_Mapper;

// Inputs
reg 		clk 				;
reg 		rstn 				;
reg 		s_config_tvalid 	;
wire 		s_config_tdata 		;
reg 		s_data_tvalid 		;
reg 		s_data_tdata 		;
reg 		s_data_tlast 		;
reg 		m_data_tready 		;

// Outputs
wire 		s_data_tready 		;
wire 		m_data_tvalid 		;
wire [3:0] 	m_data_tdata 		;
wire 		m_data_tlast 		;
reg  		s_data_tvalid1 		;
reg  		s_data_tlast1 		;
reg  		mode_type 	= 	1	;

assign s_config_tdata = mode_type;
integer i;
	initial begin
		clk = 0;
		rstn = 0;
		s_config_tvalid = 0;
		s_data_tvalid = 0;
		m_data_tready = 1;

        repeat(10) @(posedge clk);#1;
        rstn = 1;
        repeat(5) @(posedge clk);#1;
        s_config_tvalid = 1;
        repeat(1) @(posedge clk);#1;
        s_config_tvalid = 0; 
		
	 for(i=1;i<=1;i=i+1) 
	  	begin
		repeat(1)@(posedge clk);#1;	//begin	
		s_data_tvalid = 1;
		s_data_tlast = 0;
		repeat(16200*1-1)@(posedge clk);#1;	
		s_data_tvalid = 1;                  
		s_data_tlast =1;	
		repeat(1)@(posedge clk);#1;	//end
		s_data_tvalid = 0;
		s_data_tlast = 0;			
		repeat(5000*1)@(posedge clk);#1;	//IDLE
	  end

	end
 always #5 clk = ~clk;
	
//====================================================================================
//  input 
//====================================================================================	 
	always@(posedge clk)
	begin
		s_data_tvalid1 <=`UD s_data_tvalid;
		s_data_tlast1 <= `UD s_data_tlast;
	end
	
	integer fid1;
	
	initial 
	begin
	fid1 = $fopen("mapper_in.txt","r");
	end
	
	always@(posedge clk)
	begin
		if(s_data_tvalid)
			$fscanf(fid1,"%d",s_data_tdata);
	end	
//====================================================================================
//  output 
//====================================================================================
	integer fid2;
	initial 
	begin
		fid2 = $fopen("ms_mapper_out.txt","w");	
	end
	
	always@(posedge clk)
	begin
		if(m_data_tvalid)
			$fwrite(fid2,"%d\n",m_data_tdata);
	end


tx_Mapper uut (
	.clk 				(clk 				), 
	.rst_n 				(rstn 				), 
	.s_config_tvalid	(s_config_tvalid 	), 
	.s_config_tdata 	(s_config_tdata 	), 
	.s_data_tvalid 		(s_data_tvalid1 	), 
	.s_data_tdata 		(s_data_tdata 		), 
	.s_data_tready 		(s_data_tready 		), 
	.s_data_tlast 		(s_data_tlast1 		), 
	.m_data_tvalid 		(m_data_tvalid 		), 
	.m_data_tdata 		(m_data_tdata 		), 
	.m_data_tready 		(m_data_tready 		), 
	.m_data_tlast 		(m_data_tlast 		)
);
      
endmodule


這部分測試代碼的編寫沒有難度,這裏不再介紹。

FPGA與MATLAB的交叉驗證

通過閱讀MATLAB代碼,可以知道我們最後會把映射之後的信息保存到相應的mat文件,然後再Modelsim仿真的時候會將映射之後的結果保存到txt文件,那麼我們通過MATLAB代碼很容易驗證實現兩塊內容的交叉驗證。MATLAB代碼如下:

clc;
clear all;
load DataOut.mat
data_lab = DataOut;
   map_data_lab = [data_lab];
   
   fid1 = fopen('ms_mapper_out.txt','r');
   map_data_sim = fscanf(fid1,'%d');

   if(isempty(map_data_sim))
   map_data_result = 0;
   else
   map_data_result = sum(abs(map_data_lab - map_data_sim(1:length(map_data_lab))));
   end
  
  a = map_data_result;

運行之後,發現如下結果:
在這裏插入圖片描述
從而驗證了我們實驗的正確性。

小結

相信大家在學習算法的FPGA實現的時候都掌握了上面的流程,就是先在MATLAB中實現,然後再在FPGA中實現,交互驗證實現的正確性。

參考文獻

[1]、電子發燒友學院

總結

創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。或者對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章