Kraklog

GPIO , UART 본문

[Harman] 하만 반도체 설계/NiosII

GPIO , UART

Krakens 2023. 9. 9. 01:22
728x90

New processor

licenese가 없기 때문에 economy를 골라준다.

on chip memory를 올려주고 총 memory size를 바꿔준다.

 

배치 후 연결을 해준다. 
연결을 해준 후인데 아직 에러가 4개가 남았다.

 

그 중 한가지는 adress가 겹쳐서 생기는 문제이다.

address를 재할당해주면 해결된다.

 

이 에러는 on chip memory가 연결되지 않아서 생기는 문제이다.

cpu의 reset vector와 exception vector를 on chip memory와 연결해준다.

slave는 data master에 연결해준다.

 

LED 등 입출력을 위해서는 pio가 필요하다.

8bit led를 출력하기 위해서 크기는 8로 설정해주었다.

led를 바꾸고 clock과 reset, s1을 연결해준다.  반드시 conduit을 설정해줘야 출력을 볼 수 있다.

이 때 에러가 발생할 수 있는데, 보통 address 에러이다. 재할당해준다.

 

led의 핀이 할당된것을 알 수 있다.

 

 

4bit 키 입력을 위해 pio를 할당해준다.

edge capture는 동작 특성상 falling edge에서, 인터럽트를 보기 위해서 설정해준다.

 

컴파일 완료 후

핀 플래너를 통해 핀을 설정해준다.

아니면 다른 방법으로 qsf 파일을 편집하는 방식이 있다.

개인적으론 qsf를 편집하는게 더 편한듯

 

 

선택해주어 사용하지 않는 led 불이 아예 들어오지 않게 설정해주었다.

 

설정 후 컴파일

 

 

sof 파일을 올려준다.

 

 

Nios II에 새로운 프로젝트로 my_gpio를 만들어준다.

 

 

실행해주면 console 창에 "Hello from Nios II!"가 출력되어진다.

 

 

led의 address가 0x9000 임을 확인하고

 

led 출력을 위한 코드를 작성해준다. 

 

다시 run을 해주면 console 창에 Hello from Nios와 함께 LED가 출력된다.

Hello from Nios 대신에 다른 문구 등을 넣어서 기기의 문제가 없음을 출력해주면 일반적인 테스트 모듈로 생각해도 된다.

 

polling 방식으로 key 버튼 읽기

key의 address 는 0x0000

 

#include "sys/alt_stdio.h"
#include "unistd.h"

int main()
{ 
  alt_putstr("Hello from Nios II!\n");

  *(volatile unsigned int *) 0x9000 = 0xff;
  unsigned int key_v;

  /* Event loop never exits. */
  while (1){
  	  key_v = * (volatile unsigned int *) 0x000;
  	  printf("key value = 0x%x\n",key_v);
  	  usleep (80*10000);
  }
  return 0;
}

 

 

 

 

 

 

 

 

`timescale 1ns / 1ns
//`define APWM
`define gpio

module avalon_module (
    input               clk         ,
    input               rst         ,
	input               mp_waitR	,       //Avalon bus
	input       [31:0]	mp_rData	,
    output reg  [31:0]	mp_addr	    ,
	output reg  [ 3:0] 	mp_bEn      ,  
    output reg  		mp_rD		,
	output reg  		mp_wR    	,
	output reg  [31:0]	mp_wData     
);
    
    parameter FF = 1;

    initial begin
        mp_addr     =32'hx; //unknown value
        mp_bEn      =4'bx;
        mp_rD       =1'b0;
        mp_wR       =1'b0;
      //mp_waitR    =;
      //mp_rData    =;
        mp_wData    =32'hx;
    
    #200;
    `ifdef APWM    
    avalon_write(32'd0,32'd10); //set period
    avalon_write(32'd1,32'd7);  //set duty
    avalon_read(32'h0000);
    avalon_read(32'h0001);
    `elsif gpio
	avalon_write(32'h2, 32'd1);
	avalon_write(32'h3, 32'd1);
	avalon_read(32'h0);
	avalon_read(32'h2);
	avalon_read(32'h3);
	#400
	avalon_read(32'h0);
    `else
    avalon_write(32'h1000,32'hAABBCCDD);
    avalon_write(32'h1000,32'hDEADBEEF);
    avalon_read(32'h1000);
    avalon_read(32'h2000);
    `endif
    end
	
	
	//avalon write (bus modeling)
    task avalon_write;
        input   [31:0]  wAddr;
        input   [31:0]  wData;
        
        begin
            @(posedge clk);
            #(FF);		   //step 1 delay
            mp_addr     = wAddr;
			mp_bEn      = 4'b1;
			mp_wR 		= 1'b1	;
			mp_wData		= wData	;
			@(posedge clk);
			while (mp_waitR) begin  //mp_waitR == 0
				@(posedge clk);			
			end
			#(FF);
			//initial value
			mp_addr		= 32'hx ;
			mp_bEn  	= 4'bx  ;
			mp_wR 		= 1'b0  ;
			mp_wData	= 32'hx ;
		end
	
	endtask
	
    task avalon_read;
        input   [31:0]  rAddr;

        begin
            @(posedge clk);
            #(FF);		   //step 1 delay
            mp_addr     = rAddr;
			mp_bEn      = 4'b1;
			mp_rD 		= 1'b1	;
			
			@(posedge clk);
			while (mp_waitR) begin  //mp_waitR == 0
				@(posedge clk);			
			end
			#(FF);
            $display("AV rAddr 0x%x, rData 0x%x", mp_addr, mp_rData);
			//initial value
			mp_addr		= 32'hx ;
			mp_bEn  	= 4'bx  ;
			mp_rD 		= 1'b0  ;
			@(posedge clk);
        end
    endtask

endmodule
`define clk_50MHz 20
//`define regs
//`define pwms
`define gpio

module tb_avalon_module;
    reg             clk         ;
    reg             rst         ;
	//Avalon bus
	`ifdef regs
	wire           	mp_waitR	;       
	wire  [31:0]	mp_rData	;
	`elsif pwms
	wire           	mp_waitR	;       
	wire  [31:0]	mp_rData	;
	wire  [07:0]	pwm_out		;
	`elsif gpio
	wire           	mp_waitR	;   
	wire  [31:0]	mp_rData	;
	reg				in_port		;
	wire			irq			;	
	`else
	reg             mp_waitR	;       
	reg  [31:0]	    mp_rData	;
	`endif 
	wire [31:0]	    mp_wData    ;
    wire [31:0]	    mp_addr	    ;
	wire [ 3:0] 	mp_bEn      ;  
    wire 			mp_rD		;
	wire 			mp_wR    	;



avalon_module uAvalon_module (
    .clk        (clk     ),
    .rst        (rst     ),
	.mp_waitR   (mp_waitR),  
	.mp_rData   (mp_rData),
    .mp_addr	(mp_addr ),
	.mp_bEn     (mp_bEn  ),  
    .mp_rD	    (mp_rD	 ),
	.mp_wR      (mp_wR   ),
	.mp_wData   (mp_wData)   
);
`ifdef regs
my_reg uMy_reg (    
    .clk(clk)  	,
    .rst(rst)  	,
    //Avalon Bus
    .addr (mp_addr ), 
    .bEn  (mp_bEn  ),
    .rD   (mp_rD   ),
    .wR   (mp_wR   ),
    .waitR(mp_waitR),
    .rData(mp_rData),
    .wData(mp_wData) 
);
`elsif pwms
     avalon_pwm uAvalon_pwm_0 (
        .clk    (clk     ), 
        .wr_data(mp_wData), 
        .wr_n   (~mp_wR), 
        .addr   (mp_addr[0]), 
        .clr_n  (~rst), 
        .rd_data(mp_rData), 
        .pwm_out(pwm_out)
);
`elsif gpio
	avalon_gpio uAvalon_gpio (
		.clk		(clk			),
		.reset_n	(~rst			),
		.address	(mp_addr[1:0]	),
		.write_n	(~mp_wR			),
		.writedata	(mp_wData		),
		.readdata	(mp_rData		),
		.in_port	(in_port		),
		.irq		(irq			)
	);
`else
`endif
    
initial fork
	clk_gen;
	rst_gen;
	`ifdef regs
	`elsif pwms
	`elsif gpio
	data_gen;
	`else
	data_gen;
	`endif
join

task clk_gen; 
 begin
	clk = 1'b0;
	forever #(`clk_50MHz/2) clk = ~clk; 
 end
endtask

task rst_gen; 
 begin
	rst =1'b0;
	repeat(2) @(posedge clk);
	rst =1'b1;
	repeat(2) @(posedge clk);
	rst =1'b0;
 end
endtask

`ifdef regs
`elsif pwms
`elsif gpio
task data_gen;
begin
	#75;
	in_port = 1'h0;
	#500;
	in_port = 1'h1;
end
endtask
`else
task data_gen;
begin
	#75;
	mp_rData = 32'h11223344;
end
endtask
`endif
endmodule

edge capture , interrupt 
- edge detcion을 하기 위해 F/F 2개 사용. (가장 효율적이다.)

interrupt에서 reduction operator를 사용해야하는데 비트가 길어진 경우 비트 중 하나라도 1이 있으면 interrupt를 발생시켜야 하기 때문이다.

 

edge_captire & int_mask가 1일때 irq가 발생하게 된다.

`define AVALON_ABUS_SIZE	2
`define AVALON_DBUS_SIZE	32
`define INPUT_BUS_SIZE		1

module my_avalon_gpio (
	input											clk		,
	input											reset_n	,
	input		[`AVALON_ABUS_SIZE-1:0]		address	,
	input											write_n	,
	input		[`AVALON_DBUS_SIZE-1:0]		writedata,
	output 	[`AVALON_DBUS_SIZE-1:0]		readdata	,
	input		[`	INPUT_BUS_SIZE -1:0]		in_port	,
	output										irq
);

	//ADDR 0 : data in/out
	//ADDR 1 : direction -> No
	//ADDR 2 : interrupt mask
	//ADDR 3 : edgecapture
	//register => sequencial
	reg	[`	INPUT_BUS_SIZE-1:0] 			data_in	;
	reg	[`	INPUT_BUS_SIZE-1:0]			int_mask	;
	reg	[`	INPUT_BUS_SIZE-1:0]			edge_cap	;
	
	//2 Flip Flop
	reg	[`	INPUT_BUS_SIZE-1:0] 			data_in_dly0;
	reg	[`	INPUT_BUS_SIZE-1:0] 			data_in_dly1;
	
	wire  [`	INPUT_BUS_SIZE-1:0]			read_out	;
	assign read_out = (address == 2) ?	int_mask :
							(address == 3) ?  edge_cap :
							 data_in;	
					
	//data read	
	reg  	[`AVALON_DBUS_SIZE-1:0] 		reg_readdata;
	always @(posedge clk, negedge reset_n) begin
		if (!reset_n)
			reg_readdata <= 0;
		else
			reg_readdata <= {`AVALON_DBUS_SIZE{1'b0}} | read_out;
	end
	assign readdata = reg_readdata;

	//data write -> Use 3 register
	//data_in
	always @(posedge clk, negedge reset_n) begin
		if (!reset_n)
			data_in <= 0;
		else
			data_in <= in_port;
	end
	
	//interrupt mask -> CPU
	wire int_mask_str = (!write_n && (address == 2));
	always @(posedge clk, negedge reset_n) begin
		if (!reset_n)
			int_mask <= 0;
		//else if (!write_n && (address == 2))
		else if (int_mask_str)
			int_mask <= writedata;
	end
	
	//data_in delay - Flip Flop
	wire  [`	INPUT_BUS_SIZE-1:0]	edge_det = data_in_dly0 & ~data_in_dly1;
	always @(posedge clk, negedge reset_n) begin
		if (!reset_n) begin
			data_in_dly0 <= 0;
			data_in_dly1 <= 0;
		end else begin
			data_in_dly0 <= in_port;
			data_in_dly1 <= data_in_dly0;
		end
	end
	
	//edgecapture
	wire edge_cap_str = (!write_n && (address == 3));
	always @(posedge clk, negedge reset_n) begin
		if (!reset_n)
			edge_cap <= 0;
		else if (edge_cap_str)
			edge_cap <= 0;  //clear
		else if (edge_det[0])
			edge_cap[0] <= 1'b1; //On
	end
	
	//interrupt
	assign irq		= |(int_mask & edge_cap);
	assign readdata = reg_readdata;
	
endmodule

 

 

 

 

UART

 

#include "sys/alt_stdio.h"
#include "unistd.h"  //usleep을 사용하기 위함
#include "system.h"
#include "altera_avalon_uart_regs.h"

int main()
{ 
  unsigned int state;
  alt_putstr("Hello from Nios II!\n");

  /* Event loop never exits. */
  while (1) {
	  state = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
	  while (!(state & 0x40)) {  //0x40 = 6th bit
		  state = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
	  }

	  IOWR_ALTERA_AVALON_UART_TXDATA(UART_0_BASE, 'A');  //write out data
	  usleep(1*1000*1000);
  }

  return 0;
}

A출력

#include "sys/alt_stdio.h"
#include "unistd.h"
#include "system.h"
#include "altera_avalon_uart_regs.h"

int main()
{ 
  unsigned int state;
  unsigned char c;
  alt_putstr("Hello from Nios II!\n");

  /* Event loop never exits. */
  while (1) {
	  state = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
	  while (!(state & 0x40)) {  //0x40 = 6th bit
		  state = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
	  }

	  IOWR_ALTERA_AVALON_UART_TXDATA(UART_0_BASE, 'A');  //write out data
	  usleep(1*400*1000);
	  if (state & 0x80) {
		  c = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);  //rrdy : 데이터를 읽을 준비가 되어있음.
		  alt_printf("RCV data = [%c 0x%x] \n" , c, c);
	  }
  }

  return 0;
}

Interrupt 를 통해 txdata 전송을 확인.

0x80은 7bit register를 가리키므로 7 register는 read data이고, 0x80엔는 데이터가 들어와있게 된ㄴ다.

따라서 ready에 있는 값을 인터럽트를 통해 출려 시킬 수 있다.

728x90

'[Harman] 하만 반도체 설계 > NiosII' 카테고리의 다른 글

IIC - bit bang  (0) 2024.01.03
Avalon PWM, PWM Interrupt  (0) 2023.09.08
Avalon BUS + reg 설계  (0) 2023.08.28
PWM / CRC  (0) 2023.08.28
Nios Processor  (0) 2023.07.28