본문 바로가기

Verilog Projects

Avalon Bus : GPIO

Development Environment.

Program : Quartus Prime 18.1 Lite Edition, Eclipse Mars.2 Release(4.5.2)

Tool : Modelsim 10.5b Starter Edition

FPGA : Cyclone V

Hardware Devices : De1-SoC Board.

Language : Verilog2001


Objective.

Design GPIO corresponding to commercial IP and generate interrupt signals.


Why?

GPIO는 General Purpose Input/Output의 약어로 마이크로 프로세서와 외부 디바이스 간의 통신을 위해 사용된다. 특정 목적을 위해 설계된 것이 아닌 사용자가 직접 입출력에 대한 신호를 임의로 지정하여 동작을 제어할 수 있도록 해준다. 입력으로 사용되는 경우는 외부 인터럽트를 처리할 수 있도록 하는 것이 일반적이다. Interrupt 동작을 포함한 GPIO를 설계하고  De1-SoC Board로 검증하여 GPIO와 Interrupt에 대한 이해를 넓히고자 한다.


Design Steps. 

1. Block Diagram.

2. Verilog code with Avalon interface.

3. Modelsim Simulation.

4. verification with De1-SoC Board.


1. Block Diagram.

Slave > IRQ > Master

 

GPIO 내부의 data_in, interruptmask, edgecapture와 같은 레지스터들은 IP나 모듈에서 특정한 기능들을 수행하도록 설계되었다. PIO Regsiter Map을 참고하여 각 레지스터의 오프셋 및 기능을 확인할 수 있다. 

 

Register Map

 


2. Verilog Code with Avalon Interface.

 

`define AVALON_DBUS_SIZE 32
`define AVALON_ABUS_SIZE 2
`define  INPUT_DBUS_SIZE 1

module custom_gpio_in (
   input                            clk         ,
   input                            reset_n     ,
   input    [`AVALON_ABUS_SIZE-1:0] address     ,
   input                            write_n     ,
   input    [`AVALON_DBUS_SIZE-1:0] writedata   ,
   input    [ `INPUT_DBUS_SIZE-1:0] in_port     ,
   output                           irq         ,
   output   [`AVALON_DBUS_SIZE-1:0] readdata     
   
   );
   
   reg      [ `INPUT_DBUS_SIZE-1:0] data_in;
   reg      [ `INPUT_DBUS_SIZE-1:0] edge_cap;
   reg      [ `INPUT_DBUS_SIZE-1:0] int_mask;
   
   reg      [ `INPUT_DBUS_SIZE-1:0] data_in_dly0;
   reg      [ `INPUT_DBUS_SIZE-1:0] data_in_dly1;
   
   wire     [ `INPUT_DBUS_SIZE-1:0] read_out;
   
   assign read_out = (address == 2) ? int_mask :
                     (address == 3) ? edge_cap :
                                      data_in;
   
   reg      [`AVALON_DBUS_SIZE-1:0] r_readdata;
   
   always@(posedge clk, negedge reset_n) begin
      if(!reset_n)
         data_in <= 0;
      else
         data_in <= in_port;
   end
   
   wire int_mask_str = ((address == 2) && !write_n);
   always@(posedge clk, negedge reset_n) begin
      if(!reset_n)
         int_mask <= 0;
      else if(int_mask_str)
         int_mask <= writedata;
   end
      
   wire edge_det = ~data_in_dly1 & data_in_dly0;
   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
 
   wire edge_cap_str = ((address == 3) && !write_n);
   always@(posedge clk, negedge reset_n) begin
      if(!reset_n)
         edge_cap <= 0;
      else if(edge_cap_str)
         edge_cap <= 0;
      else if(edge_det)
         edge_cap <= 1'b1;
   end
   
   always@(posedge clk, negedge reset_n) begin
      if(!reset_n)
         r_readdata <= 0;
      else
         r_readdata <= {`AVALON_DBUS_SIZE{1'b0}} | read_out;
   end
   
   assign readdata = r_readdata;
   assign irq      = |(int_mask & edge_cap); 
   
endmodule

 

`timescale 1 ns / 1 ns

module Avalon_Model (
   input                clk         ,
   input                reset       ,
   output reg  [31:0]   address     ,
   output reg  [ 3:0]   byteenable  ,
   output reg           read        ,
   output reg           write       ,
   input                waitrequest ,
   input       [31:0]   readdata    ,
   output reg  [31:0]   writedata   
);
   
   initial begin
      address     = 32'hx;
      byteenable  = 4'hx;
      read        = 1'b0;
      write       = 1'b0;
      writedata   = 32'hx;
      
      repeat(3) @(posedge clk);
      
      Avalon_Write(32'h0002, 32'h1);
      Avalon_Write(32'h0003, 32'h1);
      Avalon_Read(32'h0000);
      Avalon_Read(32'h0002);
      Avalon_Read(32'h0003);
   end

   task Avalon_Write;
      input [31:0]   w_address;
      input [31:0]   w_writedata;
      
      begin
         @(posedge clk);
         #(FF);
         address     = w_address;
         byteenable  = 4'b0001;
         write       = 1'b1;
         writedata   = w_writedata;
         @(posedge clk);
         while(waitrequest)begin
            @(posedge clk);
         end
         #(FF);
         address     = 32'hx;
         byteenable  = 4'bx;
         write       = 1'b0;
         writedata   = 32'hx;
      end
   endtask
   
   task Avalon_Read;
      input [31:0]   r_address;
      
      begin
      @(posedge clk);
         #(FF);
         address     = r_address;
         byteenable  = 4'b0001;
         read        = 1'b1;
         @(posedge clk);
         while(waitrequest)begin
            @(posedge clk);
         end
         #(FF);
         $display("addr = 0x%x, rdata = 0x%x \n", address, readdata);
         address     = 32'hx;
         byteenable  = 4'bx;
         read        = 1'b0;
      end
   endtask
   
endmodule

3. Modelsim Simulation.

 

`timescale 1 ns / 1 ns

module tb_Avalon_Model_gpio();

   reg               clk;        
   reg               reset;         
   wire     [31:0]   address;    
   wire     [ 3:0]   byteenable; 
   wire              read;       
   wire              write;            
   wire     [31:0]   readdata;
   wire        waitrequest;
   wire     [31:0]   writedata;
   reg         in_port;
   wire        irq;
   
   Avalon_Model Avalon_Model_u0 (
      .clk        (clk        ),
      .reset      (reset      ), 
      .address    (address    ),
      .byteenable (byteenable ),
      .read       (read       ),
      .write      (write      ), 
      .waitrequest(waitrequest), 
      .readdata   (readdata   ), 
      .writedata  (writedata  )
   );
   
   custom_gpio_in custom_gpio_in_u0 (
      .clk      (clk          ),
      .reset_n  (!reset       ),
      .address  (address[1:0] ),
      .write_n  (!write       ),
      .writedata(writedata    ),
      .in_port  (in_port      ),
      .irq      (irq          ),
      .readdata (readdata     )
   );
   
   initial fork
      clk_gen();
      reset_gen();
      data_gen();
   join
   
   task clk_gen;
      begin
         clk = 1'b0;
         forever #10 clk = ~clk;
      end
   endtask
   
   task reset_gen;
      begin
         reset = 1'b0;
         repeat(2) @(posedge clk);
         reset = 1'b1;
         repeat(2) @(posedge clk);
         reset = 1'b0;
      end
   endtask
   
   task data_gen;
      begin
         #70;
         in_port = 1'b0;
         #220;
         in_port = 1'b1;
      end
   endtask  

endmodule

 

vlib work
vlog custom_gpio_in.v tb_Avalon_Model_gpio.v Avalon_Model.v
vsim work.tb_Avalon_Model_gpio
view wave

add wave -radix unsigned /clk
add wave -radix unsigned /reset
add wave -radix unsigned /address
add wave -radix unsigned /byteenable
add wave -radix unsigned /read
add wave -radix unsigned /write
add wave -radix unsigned /readdata
add wave -radix unsigned /writedata

add wave -radix unsigned /custom_gpio_in_u0/address
add wave -radix unsigned /custom_gpio_in_u0/write_n
add wave -radix unsigned /custom_gpio_in_u0/r_readdata
add wave -radix unsigned /custom_gpio_in_u0/in_port
add wave -radix unsigned /custom_gpio_in_u0/data_in
add wave -radix unsigned /custom_gpio_in_u0/data_in_dly0
add wave -radix unsigned /custom_gpio_in_u0/data_in_dly1
add wave -radix unsigned /custom_gpio_in_u0/edge_det
add wave -radix unsigned /custom_gpio_in_u0/int_mask
add wave -radix unsigned /custom_gpio_in_u0/int_mask_str
add wave -radix unsigned /custom_gpio_in_u0/edge_cap
add wave -radix unsigned /custom_gpio_in_u0/edge_cap_str
add wave -radix unsigned /custom_gpio_in_u0/irq

add wave -radix unsigned /custom_gpio_in_u0/int_mask
add wave -radix unsigned /custom_gpio_in_u0/edge_cap
add wave -radix unsigned /custom_gpio_in_u0/irq

run 600ns

 


4. Verification with De1-SoC Board & Eclipse.

1. Add Custom Component.

Top-level Module, Signal Type, Interface 설정에 주의

 

2. System Configuration.

Clk, CPU(Nios II Processor), Memory, JTAG(Debugging), My_gpio_0(Custom Component, GPIO)

Generate HDL > create sopcinfo, qip File.

 

3. Compilation.

1) 프로젝트에 qip File 추가하고 Platform Designer의 Instantation Template Copy 후 Analysis & Synthesis.

 

module custom_gpio (
   input clk,
   input reset_n,
   input sw
); 
   
   niosII_system uNiosII_system_0 (
        .clk_clk                          (clk    ),                         
        .custom_gpio_0_conduit_end_export (sw     ),
        .reset_reset_n                    (reset_n)                    
    );

endmodule

 

2) Pin Planner Setting

 

3) Full Compilation.

 

4. Verification With Eclipse.

 

#include "sys/alt_stdio.h"
#include "altera_avalon_pio_regs.h" // copy

#include "unistd.h"
#include "system.h"

#include "sys/alt_irq.h"
#include "priv/alt_iic_isr_register.h"

void key_isr (void)
{
	unsigned int k;
	k = IORD_ALTERA_AVALON_PIO_EDGE_CAP(CUSTOM_GPIO_0_BASE); // input sw value
	alt_printf("key_isr [0x%x] \n",k);
	IOWR_ALTERA_AVALON_PIO_EDGE_CAP(CUSTOM_GPIO_0_BASE, 0x1); // clear
}

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

  *(volatile unsigned int *)0x9000 = 0xff;
  
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(CUSTOM_GPIO_0_BASE, 0x1); // clear
  alt_iic_isr_register(
		  CUSTOM_GPIO_0_IRQ_INTERRUPT_CONTROLLER_ID,
		  CUSTOM_GPIO_0_IRQ,
  		  key_isr,
  		  NULL,
  		  NULL);          
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(CUSTOM_GPIO_0_BASE, 0x1); // masking

  while (1){
	  switch_v = *(volatile unsigned int *)0x0;
	  printf("switch values = 0x%x \n", switch_v);
	  usleep(1*500*1000);
  }
  while(1);

  return 0;
}

 

'Verilog Projects' 카테고리의 다른 글

Morse Code Generator.  (0) 2023.12.17
Avalon Bus : PWM  (0) 2023.11.12
Avalon Bus Modeling.  (1) 2023.11.12
UART.  (0) 2023.11.08
8-Bit Multiplier.  (0) 2023.10.28