SOPC之NiosⅡ系统(三)

常用NIOS Ⅱ组件概括

目录

1.定时器Timer

1.1 预定义硬件配置

1.2 超时周期Timeout poriod

1.3 计数器大小Timer counter Size

1.4 寄存器Registers

2.串口UART

2.1 基础设置Basic settings

2.1.1 奇偶校验Parity

2.1.2 数据为Data bits

2.1.3 停止位Stop bits

2.1.4 其他

2.2 波特率设置Baud rate

3.SDRAM

3.1 Memory Profile

3.2 Timing

4. 自定义IP核


1.定时器Timer

SOPC之NiosⅡ系统(三)_第1张图片

Interval timer核的基础特性:
1.Niosll 处理器的Avalon-MM主设备可以通过写控制寄存器 (control register) 来完成:(1)启动和停止定时器;(2)使能/禁止IRQ;(3)明确累减计数一次或重复性累减计数

2.处理器通过读状态寄存器 (status register) ,能够得知定时器所处的状态

3.处理器可以通过给周期寄存器 (period registers) 写数值来指定定时器的周期

4.内部计数器累减到 0,然后会从周期寄存器 (period registers) 获取数值,继续累减计数

5.处理器可以通过一些操作来获取当前计数器 (counter) 的值:先给其中一个捕获寄存器 (snap registers)写数据,然后读捕获寄存器 (snap registers) 来获得完整的值。

6.当计数器计数到 0 的时候,可能会触发:(1)如果IRQ被使能,此时会触发 IRQ;(2)可选的一个时钟周期的脉冲输出;(3)可选的看门狗输出,复位系统

 在IP Catalog中搜索Timer添加Interval Timer Intel FPGA IP组件

SOPC之NiosⅡ系统(三)_第2张图片

1.1 预定义硬件配置

Library中提供了预定义的硬件配置:
Simple periodic interrupt (简单周期中断)一一用于只需要周期性地触发 RQ 的系统,固定周期,定时器无法被停止,但IRQ可以被禁止。
Full-featured (全功能) 一一用于要求定时器能被处理器启动或停止,但能禁止IRQ的嵌入式处理器系统
Watchdog(看门狗)一一用于需要“看门狗”定时器去复位停止响应的系统

1.2 超时周期Timeout poriod

超时周期(Timeout Period) 是定时器频率 (Timer Frequency)的整数倍。定时器频率固定为系统时钟的频率。定时器频率设置的单位可以是μs、ms、seconds或者clocks (系统时钟周期个数)


Period定时器周期,没有选中 Fixed period 选项的话,可以在软件中修改。若软件中没有修改操作,period 中的值就会作为超时周期 (Timeout Period)
Units: 周期的单位,可以是μs、ms、seconds、clocks。

SOPC之NiosⅡ系统(三)_第3张图片

1.3 计数器大小Timer counter Size

Counter Size设置决定了定时器的位宽,可以设置成32位或 64 位。

32位的定时器拥有2个16位的period registers (周期寄存器)

64位的定时器拥有4个16位的period registers (周期寄存器)

1.4 寄存器Registers


No Start/Stop controlbits:没有选中该选项时,主设备可以通过写control寄存器中的START和 STOP位来启动或者停止定时器。当选中该选项后,定时器会一直运行。当硬件中使能了“watchdog功能后,无论No Start/Stop control bits选项如何,control寄存器中 START 位有效。

Fixed period:选中该选项后,就无法在软件中更改定时器周期,且定时器周期为Timeout Period处设置的结果;若没有选中该选项,可以通过在软件中给 period registers 赋值来更改定时器周期(timeout Period )

Readable snapshot: 选中该选项后,主设备可以读取当前的计数值。没有选中该选项时,硬件中将不会存在 snap 寄存器,且读 snap 寄存器会返回未定义的值。在这种配置下,计数器 (counter)的状态只能通过status寄存器或IRQ信号之类的来观测

2.串口UART

在IP Catalog中搜索uart添加UART(RS-232 Serial Port) Intel FPGA IP组件

SOPC之NiosⅡ系统(三)_第4张图片

SOPC之NiosⅡ系统(三)_第5张图片

2.1 基础设置Basic settings

2.1.1 奇偶校验Parity

Partity有None (无)、Even (偶)、Odd (奇) 三个选项

用来确定UART是否发送有奇偶校验的字符,以及它是否期望接收到的有奇偶校验的字符。 

SOPC之NiosⅡ系统(三)_第6张图片

2.1.2 数据为Data bits

Data bits有7、8、9三个可设置选项,这个设置决定了txdata、rxdata、endofpacket三个寄存器的位宽。

2.1.3 停止位Stop bits

Stop bits有 1、2两个选项,决定了IP 核在传输每一个字符时,是有一个还是两个停止位。

UART IP 核总是在接收到第一个停止位的时候,就停止接收操作,忽略掉附带的停止位,无论什么设置。

2.1.4 其他

Synchronizer Stages与寄存器的长度以及亚稳态事件相关,一般使用默认设置。

Include CTSRTS选择是否使用串口的“流控”功能,一般很少使用

Include end-of-packet选择是否设置数据流的结束标志 (end-of-packet),一般很少使用

2.2 波特率设置Baud rate

UART 内核可实现RS-232标准中的任意波特率。

SOPC之NiosⅡ系统(三)_第7张图片

波特率可配置为:

固定的波特率一一波特率在系统生成时被确定,且不能通过 Avalon 从控制器端口改变它的值。
可变的波特率一一基于divisor寄存器中存储的时钟分频值,波特率是可变的。主控制器通过向divisor寄存器中写入新值来改变波特率。

波特率的计算依赖于 Avalon-MM 接口提供的时钟频率。在硬件改变系统时钟频率,却没有重新生成UART IP 核会导致错误的信号。

Baud Rate设置决定了复位后的波特率。Baud Rate选项提供了标准的预设值。也允许用户输入任何非标准波特率。为了实现所需要的波特率,通常根据波特率计算时钟分频系数。

波特率与分频系数的关系如下.
除数=int ( (时钟频率) / (波特) + 0.5 )
波特率=(时钟频)/(除 + 1 )

选择Fixed baud rate时,UART 硬件中不再包括divisor寄存器。UART硬件使用固定的波特率分频系数,且在系统生成后无法改变。

不选择Fixed baud rate时,硬件中会在地址偏移值4生成一个 16 位的divisor寄存器。divisor寄存器是可写的,可以通过向分频寄存器写入新值来改变波特率。

3.SDRAM

在IP Catalog中搜索sdram添加SDRAM Controller Intel FPGA IP组件

SOPC之NiosⅡ系统(三)_第8张图片

 SOPC之NiosⅡ系统(三)_第9张图片

SDRAM 控制器 IP核产生于 FPGA 内部,它带有接口引脚、控制逻辑、以及Avalon从机接口

接口引脚用来连接外部 SDRAM 芯片管脚,这些接口引脚通过Altera FPGA上的I/0 引脚连接到 SDRAM芯片管脚上。

控制逻辑用来实现SDRAM操作,比如SDRAM 初始化,自刷新,突发读写等,这些全都是控制逻辑来完成的,当生成 SDRAM 控制器 IP 核之后,控制逻辑会自动生成。

Avalon从机接口用来连接CPU,Avalon从机接口是SDRAM控制器IP核中仅为用户可见的部分。从控制器端口提供一个线性存储器空间。Avalon接口作为存储器接口操作,SDRAM芯片必须和Avalon接口一样以相同的时钟来驱动,片内锁相环(PLL)就是用来调整 SDRAM 控制器与SDRAM 芯片之间的时钟相位差。

在较低的时钟频率下,可能不需要 PLL。在较高的时钟频率下,当信号在引脚上有效时,需要 PLL 来调整SDRAM时钟相位。PLI并没有包括在 SDRAM 控制器内。如果需要PLL,设计者必须在生成Qsys系统模块以外手动添加 PLL。

SOPC之NiosⅡ系统(三)_第10张图片

Presets列表提供几个预定义的SDRAM配置。如果实际使用的SDRAM芯片型号与列表中的一致,可直接选用而不用设置其他选项。选择不同的预配置,SDRAM IP核将自动改变Memory Profile和 Timing 选项卡上的值来匹配指定的配置。如果实际使用的 SDRAM 芯片与列表中的不相同,则可以根据 SDRAM芯片数据手册的参数来设置 Memory Profile 和 Timing标签上的值。

3.1 Memory Profile

数据宽度Data Width,有8、16、32、64四个可选项(SDRAM数据总线宽度),确定dq(数据)总线和dqm总线的宽度。

SOPC之NiosⅡ系统(三)_第11张图片

体系结构Architecture:

片选chip select,有1、2、4、8四个可选项(SDRAM 芯片的数目),通过使用多个片选信号,SDRAM控制器可组合多个SDRAM芯片为一个存储器子系统。

Banks,有2、4两个可选项(Bank 的数量),该值确定连接到SDRAM的 ba (Bank 地址) 总线宽度。

地址宽度Address Width:

行row,有11、12、13、14四个可选项,行地址线的宽度,该值确定addr总线的宽度

列column,≥8且小于行值,列地址线的宽度

Generic Memory model,当打开选项时,Qsys创建SDRAM芯片的功能仿真模型,仅用于仿真。

上述参数值可参照使用的 SDRAM 手册来设置,设置完毕后会显示SDRAM预期的内存容量。

3.2 Timing

设置 SDRAM 芯片的时序规范

 SOPC之NiosⅡ系统(三)_第12张图片

4. 自定义IP核

自定义seg_decoder

首先要使用硬件描述语言描述硬件逻辑,一个典型的IP核的硬件逻辑一般由三个功能模块组成:

接口文件:作为顶层文件,定义总线接口信号;

寄存器文件:完成该IP核与外部信号进行通信,有了寄存器文件,用户就可以通过Avalon接口采用基地址+地址偏移量的方式来访问组件内部各寄存器;

硬件逻辑文件:实现IP核的硬件功能。

4.1自定义IP核实现动态数码管显示

4.1.1 创建工程及功能文件

创建Quartus工程,创建三个功能文件

顶层文件segled_controller.v

module segled_controller(
input clk, // 时钟信号
input rst_n, // 复位信号

//Avalon-MM 接口
input [ 1:0] avs_address, // Avalon 地址总线
input avs_write, // Avalon 写请求
input [31:0] avs_writedata, // Avalon 写数据

//数码管接口
output [ 5:0] sel, // 数码管位选
output [ 7:0] seg_led // 数码管段选
);

//wire define
wire [19:0] data; // 6 个数码管要显示的数值
wire [ 5:0] point; // 小数点显示的位置,从高(左)到低(右),高电平有效
wire sign; // 显示符号位(高电平显示负号)
wire en; // 数码管使能信号

//*****************************************************
//** main code
//*****************************************************

//数码管寄存器文件
segled_register u_segled_register(
.clk (clk),
.rst_n (rst_n),

//Avalon-MM 接口 
.avs_address (avs_address),
.avs_write (avs_write),
.avs_writedata (avs_writedata),

//用户接口
.data (data),
.point (point),
.sign (sign),
.en (en),
);

//数码管逻辑功能文件
segled_logic u_segled_logic(
.clk (clk), 
.rst_n (rst_n),

//用户接口
.data (data), 
.point (point),
.sign (sign),
.en (en), 

//数码管接口
.sel (sel), 
.seg_led (seg_led) 
);

endmodule

硬件逻辑文件segled_logic.v 

module segled_logic(
//module clock
	input clk , // 时钟信号
	input rst_n , // 复位信号(低有效)

//seg_led interface
	output reg [5:0] sel , // 数码管位选端(选择的数码管)
	output reg [7:0] seg_led, // 数码管段选端(数码管数值显示的段)

//user interface
	input [19:0] data , // 6 个数码管要显示的数值
	input [ 5:0] point , // 小数点显示的位置,从左到右,高电平有效
	input sign , // 显示符号位(高电平显示“-”号)
	input en // 数码管使能信号
);
 
//parameter define
localparam MAX_NUM = 13'd5000 ; // 1ms 计数值
localparam CLK_DIVIDE = 4'd10 ; // 时钟分频
 
//reg define
reg [12:0] cnt0 ; // 1ms 计数
reg 		  flag ; // 1ms 计满标志信号
reg [2:0]  cnt ; // 切换显示数码管用
reg [3:0]  num1 ; // 送给要显示的数码管,要亮的灯
reg 		  point1 ; // 要显示的小数点
reg [23:0] num ; // 24 位 bcd 码用寄存器
reg [ 3:0] clk_cnt ; // 时钟计数
reg 		  dri_clk ; // 驱动数码管操作的驱动时钟
 
//wire define
wire [3:0] data0 ; // 十万位数
wire [3:0] data1 ; // 万位数
wire [3:0] data2 ; // 千位数
wire [3:0] data3 ; // 百位数
wire [3:0] data4 ; // 十位数
wire [3:0] data5 ; // 个位数
 
//*****************************************************
//** main code
//*****************************************************

assign data5 = data[19:0] / 17'd100000;        // 十万位数
assign data4 = data[19:0] / 14'd10000 % 4'd10; // 万位数
assign data3 = data[19:0] / 10'd1000 % 4'd10 ; // 千位数
assign data2 = data[19:0] / 7'd100 % 4'd10 ;   // 百位数
assign data1 = data[19:0] / 4'd10 % 4'd10 ;    // 十位数
assign data0 = data[19:0] % 4'd10; 				  // 个位数

//生成数码管的驱动时钟用于驱动数码管的操作
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		dri_clk <= 1'b1;
		clk_cnt <= 4'd0;
	end
	else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
		clk_cnt <= 4'd0;
		dri_clk <= ~dri_clk;
	end
	else
		clk_cnt <= clk_cnt + 1'b1;
end

//将 20 位 2 进制数转换为 8421bcd 码
always @ (posedge dri_clk or negedge rst_n) begin
	if (!rst_n)
		num <= 24'b0;
	else begin
		if (data5 || point[5]) begin
			num[23:20] <= data5;
			num[19:16] <= data4;
			num[15:12] <= data3;
			num[11:8] <= data2;
			num[ 7:4] <= data1;
			num[ 3:0] <= data0;
		end
		else begin
			if (data4 || point[4]) begin
				num[19:0] <= {data4,data3,data2,data1,data0};
				if(sign)
					num[23:20] <= 4'd11;
				else
					num[23:20] <= 4'd10;
			end
			else begin
				if (data3 || point[3]) begin
						num[15: 0] <= {data3,data2,data1,data0};
						num[23:20] <= 4'd10;
				if(sign)
						num[19:16] <= 4'd11;
				else
						num[19:16] <= 4'd10;
				end
				else begin
					if (data2 || point[2]) begin
						num[11: 0] <= {data2,data1,data0};
						num[23:16] <= {2{4'd10}};
						if(sign)
							num[15:12] <= 4'd11;
						else
							num[15:12] <= 4'd10;
					end
					else begin
						if (data1 || point[1]) begin
							num[ 7: 0] <= {data1,data0};
							num[23:12] <= {3{4'd10}};
							if(sign)
								num[11:8] <= 4'd11;
							else
								num[11:8] <= 4'd10;
							end
							else begin
								num[3:0] <= data0;
								if(sign)
									num[23:4] <= {{4{4'd10}},4'd11};
								else
									num[23:4] <= {5{4'd10}};
							end
						end
					end
				end
			end
		end
end

//计数 1ms
always @ (posedge dri_clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
			flag <= 1'b0;
			cnt0 <= 13'b0;
	end
	else if (cnt0 < MAX_NUM - 1'b1) begin
			flag <= 1'b0;
			cnt0 <= cnt0 + 1'b1;
	end
	else begin
			flag <= 1'b1;
			cnt0 <= 13'b0;
	end
end

//计数器,用来计数 6 个状态(因为有 6 个灯)
always @ (posedge dri_clk or negedge rst_n) begin
	if (rst_n == 1'b0)
			cnt <= 3'b0;
	else if(flag) begin
		if(cnt < 3'd5)
			cnt <= cnt + 1'b1;
	else
			cnt <= 3'b0;
	end
end

//6 个数码管轮流显示,完成刷新( 从右到左)
always @ (posedge dri_clk or negedge rst_n) begin
	if(!rst_n) begin
		sel <= 6'b000000;
		num1 <= 4'b0;
	end
	else begin
		if(en) begin
			case (cnt)
				3'd0: begin
					sel <= 6'b111110;
					num1 <= num[3:0] ;
					point1 <= ~point[0] ;
				end
				3'd1: begin
					sel <= 6'b111101;
					num1 <= num[7:4] ;
					point1 <= ~point[1] ;
				end
				3'd2: begin
					sel <= 6'b111011;
					num1 <= num[11:8];
					point1 <= ~point[2] ;
				end
				3'd3: begin
					sel <= 6'b110111;
					num1 <= num[15:12];
					point1 <= ~point[3] ;
				end
				3'd4: begin
					sel <= 6'b101111;
					num1 <= num[19:16];
					point1 <= ~point[4];
				end
				3'd5: begin
					sel <= 6'b011111;
					num1 <= num[23:20];
					point1 <= ~point[5];
				end
				default: begin
					sel <= 6'b000000;
					num1 <= 4'b0;
					point1 <= 1'b1;
				end
		endcase
	end
	else
		sel <= 6'b111111;
	end
end

//数码管显示数据
always @ (posedge dri_clk or negedge rst_n) begin
	if (!rst_n)
		seg_led <= 7'h40;
	else begin
		case (num1)
			4'd0 : seg_led <= {point1,7'b1000000};
			4'd1 : seg_led <= {point1,7'b1111001};
			4'd2 : seg_led <= {point1,7'b0100100};
			4'd3 : seg_led <= {point1,7'b0110000};
			4'd4 : seg_led <= {point1,7'b0011001};
			4'd5 : seg_led <= {point1,7'b0010010};
			4'd6 : seg_led <= {point1,7'b0000010};
			4'd7 : seg_led <= {point1,7'b1111000};
			4'd8 : seg_led <= {point1,7'b0000000};
			4'd9 : seg_led <= {point1,7'b0010000};
			4'd10: seg_led <= 8'b11111111;
			4'd11: seg_led <= 8'b10111111;
			default : seg_led <= {point1,7'b1000000};
		endcase
	end
end

endmodule

寄存器文件segled register.v 

module segled_register(
	input clk, // 时钟信号
	input rst_n, // 复位信号(低有效)

//Avalon-MM 接口
	input [ 1:0] avs_address, // Avalon 地址
	input avs_write, // Avalon 写请求
	input [31:0] avs_writedata, // Avalon 写数据

//用户接口
	output reg [19:0] data, // 6 个数码管要显示的数值
	output reg [ 5:0] point, // 小数点显示的位置,从左到右,高电平有效
	output reg sign, // 显示符号位(高电平显示“-”号)
	output reg en // 数码管使能信号
);

//*****************************************************
//** main code
//*****************************************************

//用于给进行赋值
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		data <= 20'd0; //显示数值寄存器
		point <= 6'd0; //小数点位置寄存器
		sign <= 1'b0; //符号使能寄存器
		en <= 1'b0; //显示使能寄存器
	end
	else if(avs_write) begin
		case(avs_address)
			2'd0: //地址 0:显示数值寄存器
				data <= avs_writedata[19:0]; 
			2'd1: //地址 1:小数点位置寄存器
				point <= avs_writedata[5:0]; 
			2'd2: //地址 2:符号使能寄存器
				sign <= avs_writedata[0]; 
			2'd3: //地址 3:显示使能寄存器
				en <= avs_writedata[0];
		endcase
	end
end

endmodule

SOPC之NiosⅡ系统(三)_第13张图片

 4.1.2 使用IP核编辑器封装硬件逻辑

打开Platform Designer,选择New Component

SOPC之NiosⅡ系统(三)_第14张图片

Component Type中填入相关信息

Name(名称) 、Display name(显示名称)、Version(版本号) 、Group(分组) 、Description(描述) 、Created by(创建者) 、Icon(组件图标)、Documentation(组件文档连接)

SOPC之NiosⅡ系统(三)_第15张图片

 在Files中

首先点击Add File..将三个功能文件添加进来,如果没有选择顶层顶层文件还要在Attributesno attributes的设置顶层文件,然后点击Analyze Synthesis Files对三个文件进行分析SOPC之NiosⅡ系统(三)_第16张图片

如果代码没有问题就会分析成功

但还是会报错,这些错误后悔会进行解决

 SOPC之NiosⅡ系统(三)_第17张图片在 在Parameters页面中可以设置组件源码中定义的参数是否用户加载组件时可配置

SOPC之NiosⅡ系统(三)_第18张图片

在Signals&Interfaces中,

avalon_slave_0是Avalon-MM 总线的信号接口,Signals Type中要指定各个信号对应的类型

conduit_end是输出到Qsys系统外部的接口,是Qsys系统和外部接口的信号,通常是连接到FPGA引脚上的信号,它的 Signal Type 固定为 *(export)

clock_sink 为时钟信号,reset_sink 为复位信号

rst_n信号出现在了avalon_slave_0的interface中,这样就不能设置其Sign Type

SOPC之NiosⅡ系统(三)_第19张图片

 因此添加Reset Input,然后把rst_n拖动到该Interface中,然后将Sign Type选择为reset

 SOPC之NiosⅡ系统(三)_第20张图片

 SOPC之NiosⅡ系统(三)_第21张图片

同理添加Conduit Interface,将sel和sel_led拖入,点击conduit_end,设置其Associated Reset为reset_sink,然后再设置seg_led和sel的Sign Type为export

这里会有一个export信号类型问题,expert信号需要自定义名字,每个名字只能对应一个端口,需要给每个export分配不同名字

SOPC之NiosⅡ系统(三)_第22张图片

 同理设置avalon_slave的Associated Reset为reset_sink,至此就没有报错,点击下方finish生成自定义IP核

SOPC之NiosⅡ系统(三)_第23张图片

 

你可能感兴趣的:(一般人学不会的FPGA,fpga开发,嵌入式硬件)