본문 바로가기

Verilog Projects

Avalon Bus Modeling.

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.

Grammer : Verilog2001


Objective.

Design an Avalon Bus and verify read/write transfer of data through Modelsim & Eclipse.


Why?

Bus는 전기적 신호를 공유하며 하드웨어 블록 및 모듈과 같은 여러 IP들이 상호 데이터를 주고 받기 위해 연결된 하나의 선이다. Bus를 사용함으로써 복잡한 시스템의 인터페이스를 최적화하고 이를 통해, 데이터의 전송 및 설계 프로세스를 간소화할 수 있다.


Design Steps.

1. Avalon Interface overview.

2. Avalon Memory-Mapped Interface Signal Roles.

3. Block Diagram.

4. Verilog Coding.

5. Simulation & Verification with Modelsim.

6. Verification with De1-SoC Board & Eclipse.


1. Avalon Interfaces overview.

Avalon Interface는 Nios II Processor에서 사용하는 Interfaces 중 하나로 Intel FPGA 내 컴포넌트들의 연결을 통해 시스템 설계를 간소화해주는 프로토콜 및 데이터 형식을 의미한다. 그 중에서 Avalon Memory-Mapped Interface는 주소 기반 데이터의 read/write 동작을 구현하는 전형적인 Master-Slave Connection Interface를 말한다.

Avalon Interface / Bus

 

Nios II Processor를 활용하여 시스템을 설계하기 전에 먼저 Avalon Memory-Mapped Interface Spec에 맞는 Bus Model을 설계하고 Simulation waveform을 통해 Avalon Bus Data의 read/write 동작을 확인하도록 한다. 


2. Avalon Memory-Mapped Interface Signal Roles.

Signal Role은 Avalon Memory-Mapped Interface의 Master와 Slave 포트들의 Signal type을 나타낸다. 모든 Signal들을 사용하는 것도 아니고 모든 신호들을 반드시 필요로 하는 것도 아니다. 하지만 해당 인터페이스가 read-only일 때는 최소한 readdata가 필요하고, write-only일 때는 write, writedata가 최소한으로 필요하다. 

 

Read and Write Transfer with waitrequest


3. Block Diagram.

Avalon Model Block Diagram

 

waitrequest, readdata 신호는 Slave에서 Master로 반환되는 신호이다. 하지만 Avalon Model에서는 Master로 신호를 반환할 Slave가 없기 때문에 레지스터를 추가하여 완전한 Master-Slave Connection을 구성하도록 하겠다. 

 

Master Slave connection Avalon Model


4. Verilog Coding.

Read and Write Transfer with waitrequest

 

`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   
);

   parameter FF = 1;
   
   initial begin
      address     = 32'hx;
      byteenable  = 4'hx;
      read        = 1'b0;
      write       = 1'b0;
      writedata   = 32'hx;
      
      repeat(3) @(posedge clk);
      
      Avalon_Write(32'h0000, 32'hf);
      Avalon_Write(32'h0001, 32'h8);
      Avalon_Read(32'h0002);
      Avalon_Read(32'h0003);
   end

   task Avalon_Write;
      input [31:0]   w_address;
      input [31:0]   w_writedata;
      
      begin
         #(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;
         @(posedge clk);
      end
   endtask
   
   task Avalon_Read;
      input [31:0]   r_address;
      
      begin
         #(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;
         @(posedge clk);
      end
   endtask
   
endmodule

 

`timescale 1 ns / 1 ns

module tb_Avalon_Model();
   reg            clk         ;        
   reg            reset       ;        
   
   wire  [31:0]   address     ;     
   wire  [ 3:0]   byteenable  ;  
   wire           read        ;
   wire           write       ;        
   reg            waitrequest ;  
   reg   [31:0]   readdata    ;     
   wire  [31:0]   writedata   ;  

   Avalon_Model uAvalon_Model_0(
      .clk        (clk        ),
      .reset      (reset      ),
      .address    (address    ),
      .byteenable (byteenable ),
      .read       (read       ),
      .write      (write      ),
      .waitrequest(waitrequest),
      .readdata   (readdata   ),
      .writedata  (writedata  )
   );
   
   initial fork
      clk_gen_task();
      reset_task();
      data_task();
   join
   
   task clk_gen_task;
      begin
         clk = 1'b0;
         forever #10 clk = ~clk;
      end
   endtask
   
   task reset_task;
      begin
         reset = 1'b0;
         repeat(1) @(posedge clk);
         reset = 1'b1;
         repeat(1) @(posedge clk);
         reset = 1'b0;
      end
   endtask
   
   task data_task;
      begin
         readdata = 32'hx;
         repeat(8) @(posedge clk);
         readdata = 32'h000000AC;
         @(posedge clk);
         readdata = 32'hx;
         @(posedge clk);
         readdata = 32'h000000CC;
         @(posedge clk);
         readdata = 32'hx;
      end
   endtask
   
endmodule

 

위에서 설명한 것처럼 Avalon Model 자체만으로는 Slave가 없기 때문에 waitrequest, writedata 신호를 받을 수 없다. 따라서 레지스터를 추가하여 해당 신호를 Master로 반환할 Slave를 만들었다. 레지스터의 블록 다이어그램과 코드는 아래와 같다. 

Master Slave connection Avalon Model

 

module rReg(
   input                clk            ,
   input                reset_n        ,
   input       [31:0]   r_address      ,
   input       [ 3:0]   r_byteenable   ,
   input                r_read         ,
   input                r_write        ,
   input       [31:0]   r_writedata    ,
   output               r_waitrequest  ,
   output reg  [31:0]   r_readdata      
);
   reg         [31:0]   r_reg;
   
   always@(posedge clk, negedge reset_n)begin
      if(!reset_n)
         r_reg <= 0;
      else if(r_write)
         r_reg <= r_writedata;
      else
         r_reg <= r_reg;
   end
      
   always@(posedge clk, negedge reset_n)begin
      if(!reset_n)
         r_readdata <= 32'hx;
      else if(r_read)
         r_readdata <= r_reg;
      else
         r_readdata <= 32'hx;
   end
endmodule

 

`timescale 1 ns / 1 ns

module tb_Avalon_Model_rReg();
   reg            clk         ;        
   reg            reset       ;        
   wire  [31:0]   address     ;     
   wire  [ 3:0]   byteenable  ;  
   wire           read        ;
   wire           write       ;        
   wire           waitrequest ;  
   wire  [31:0]   readdata    ;     
   wire  [31:0]   writedata   ;  


   Avalon_Model uAvalon_Model_0(
      .clk          (clk        ),
      .reset        (reset      ),
      .address      (address    ),
      .byteenable   (byteenable ),
      .read         (read       ),
      .write        (write      ),
      .waitrequest  (waitrequest),
      .readdata     (readdata   ),
      .writedata    (writedata  )
   );
   
   rReg urReg_0(
      .clk          (clk        ),
      .reset_n      (!reset     ),
      .r_address    (address    ),
      .r_byteenable (byteenable ),
      .r_read       (read       ),
      .r_write      (write      ), 
      .r_writedata  (writedata  ),
      .r_waitrequest(waitrequest), 
      .r_readdata   (readdata   )
   ); 
      
   initial fork
      clk_gen_task();
      reset_task();

   join
   
   task clk_gen_task;
      begin
         clk = 1'b0;
         forever #10 clk = ~clk;
      end
   endtask
   
   task reset_task;
      begin
         reset = 1'b0;
         repeat(1) @(posedge clk);
         reset = 1'b1;
         repeat(1) @(posedge clk);
         reset = 1'b0;
      end
   endtask
   
endmodule

 

vlib work
vlog Avalon_Model.v tb_Avalon_Model_rReg.v rReg.v
vsim work.tb_Avalon_Model_rReg
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 /waitrequest
add wave -radix hex      /writedata 
add wave -radix unsigned /tb_Avalon_Model_rReg/uRReg_0/r_reg
add wave -radix hex      /readdata  

run 300 ns

 

5. Simulation & Verification with Modelsim.

 

write 신호 발생 시, 0번 주소와 1번 주소의 data가 정상적으로 write되었음을 확인했지만 Slave가 없기 때문에 Master로 반환할 2번 주소와 3번 주소의 readdata를 하드 코딩(AC, CC)했다. readdata 이외의 나머지 Signal들은 정상적으로 동작함을 확인할 수 있었다.

 

 

Avalon Bus Model로 read 신호 발생 시, Master로 반환되는 readdata까지 확인하기 위해 레지스터를 추가하여 완전한 Master-Slave 연결을 구성하고 이전과 동일하게 Modelsim을 통해 파형을 확인한 후, Eclipse로 검증까지 진행하도록 하겠다. 먼저 이전에 설계한 Avalon Model Test Bench에 레지스터를 Instantiation하고 Modelsim으로 Simulation 파형을 확인한다. 

 

 

Memory와 연결된 상태가 아니기 때문에 Avalon Bus Model 설계 시 임의로 할당한 address와 writedata가 출력되는 것을 확인할 수 있고 read 신호 발생 시 레지스터에 저장된 데이터가 readdata로 출력되는 것 또한 확인할 수 있다. 

 

Transcript


6. Verification with De1-SoC Board & Eclipse.

위에서 설계했던 Avalon Model과 rReg는 각각 Master와 Slave에 해당한다. Data의 read/write Transfer를 검증하기 위해 각각의 Verilog Code로 Component를 생성하고 연결해서 시스템을 구성해야한다. 하지만 Master(Avalon Model)는 이미 Nios II Processor가 그 역할을 대신하고 있으므로 Slave(rReg)만 생성하여 시스템에 추가하도록 한다.

 

system contents
pin planner, programmer
eclipse verification C code

 

#include <stdio.h>
#include <unistd.h>
#include "system.h"

int main(){
    unsigned int readdata;
    
    *(volatile unsgined int*)0x00009008 = 0xAABBCCDD;
    readdata = *(volatile unsigned int*)0x00009008;

    printf("0x9008 readdata = 0x%x \n", readdata);

    *(volatile unsigned int*)0x0000900C = readdata;
    readdata = *(volatile unsigned int*)0x0000900C;

    printf("0x900C readdata = 0x%x \n", readdata);

    *(volatile unsgined int*)0x0000900C = 0xDEADBEEF;
    readdata = *(volatile unsigned int*)0x0000900C;

    printf("0x900C readdata = 0x%x \n", readdata);

    *(volatile unsigned int*)0x00009008 = readdata;
    readdata = *(volatile unsigned int*)0x00009008;

    printf("0x9008 readdata = 0x%x \n", readdata);

    while(1)
   {

   } 
}

Debug result

 

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

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