본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 10일차 - Register, Memory

by Graffitio 2023. 6. 30.
[Harman 세미콘 아카데미] 10일차 - Register, Memory
728x90
반응형
[Register]

 

Register란?

FF 여러 개를 일렬로 배열하여 적당히 연결함으로써, 여러 비트로 구성된 2진수를 저장할 수 있도록 한 것

즉, 외부로부터 들어오는 데이터를 저장하거나 이동하는 목적으로 사용하며, 상태의 순서적인 특성을 갖는 것은 아니다.

다음과 같은 경우에 많이 사용

- CPU 내부에서 연산의 중간 결과를 임시 저장하는 경우

- 어떤 2진수의 보수를 구하는 경우

- 곱셉 또는 나눗셈을 하는 경우 등

 

Register의 종류

 

1. 직렬 입력 - 직렬 출력 레지스터

    = SI-SO Register(serial input - serial output)

 

 

module shift_register_SISO( /// SISO register 구조적 모델링
    input clk, rst, d,
    output q
);
//    wire w1, w2, w3;  // 이렇게 wire 선언해줘도 된다.
    wire [2:0] w;
    D_flip_flop_n SS1(.d(d), .clk(clk), .rst(rst), .q(w[2]));
    D_flip_flop_n SS2(.d(w[2]), .clk(clk), .rst(rst), .q(w[1]));
    D_flip_flop_n SS3(.d(w[1]), .clk(clk), .rst(rst), .q(w[0]));
    D_flip_flop_n SS4(.d(w[0]), .clk(clk), .rst(rst), .q(q));

endmodule

module shift_register_SISO_b_n( /// SISO register 동작적 모델링
    input clk, rst, d,
    output reg q
);    
    reg [3:0] siso;
    
//    always@(negedge clk, posedge rst) begin /// 이렇게 만들면 안 된다~
////        if(rst) siso = 0;       /// blocking으로 만들면 안돼
////        else begin				/// 4bit가 다 1이 되어버린다.
////            siso[3] = d;         
////            siso[2] = siso[3];   
////            siso[1] = siso[2];
////            siso[0] = siso[1];
////            q = siso[0];
////        end
////    end 

     always@(negedge clk, posedge rst) begin 
        if(rst) siso = 0;
        else begin
            siso[3] <= d;
            siso[2] <= siso[3];
            siso[1] <= siso[2];
            siso[0] <= siso[1];
            q = siso[0];
        end
    end    
endmodule

blocking으로 만들면, 이렇게 DFF 하나만 생성된다.
non-blocking으로 생성

 

2. 병렬 입력 - 직렬 출력 레지스터

    = PI-SO Register(parallel input - serial output)

       일반적인 시리얼 통신 시, 사용한다.

        - 칩 내부에서는 병렬로 입력받기 가능하니, PI

        - 통신선 1개니까 SO

 

<SH/LD'>

- SH : SH = 1일때, shift하여 출력

- LD' : LD = 0일때, load하여 데이터 입력

- MUX의 select로 사용 된다.

 

register module coding

module shift_register_PISO( /// 병렬 입력 - 직렬 출력
    input clk, rst, w_piso, /// 입력은 w(write), 출력은 r(read)로 쓴다.
    input [3:0] d,
    output q
);
    reg [3:0] data;    
    always @ (posedge clk, posedge rst) begin
        if(rst) data = 0;
        else if(w_piso) data = {1'b0, data[3:1]}; //// 클락마다 병렬로 입력된 것이 한 bit씩 출력된다.
             else data = d;
    end
    
    assign q = data[0];
    
endmodule

 

Test bench

module tb_shift_register_PISO();    
    reg [3:0] d;
    reg clk, rst, w_piso;    
    wire q; /// 입력은 reg, 출력은 wire
     
    shift_register_PISO piso_DUT(clk, rst, w_piso, d, q);
    
    initial begin
        clk = 0;
        d = 4'b1010;
        rst = 1;
        w_piso = 0; //// 로드된다.
    end
    always #4 clk = ~clk; /// sys clk가 8ns이니까, 4ns 주어야 한다.
    
    initial begin
        #8
       rst = 0; #8; /// 로드되는 걸 봐야 되니까, 여유 시간을 좀 준다.
       w_piso = 1'b1; #32; /// 4개 밀려나가야되니까, 32ns 이상 주면 된다.
       d =4'b1100; #8;
       w_piso = 0; #8
       w_piso = 1; #32;        
       $stop;
    end
endmodule

Simulation

   ※ Test bench sequence

        1. DUT instance

        2. 입력은 reg, 출력은 wire로 선언

        3. 초기화

        4. clk 생성

        5. test 하고싶은 값 넣고 시뮬레이션

 

3. 직렬 입력 - 병렬 출력 레지스터

    = SI-PO Register( serial input - parallel output)

SI-PO Register

※ Buffer

1. Buffer 

    - 타이밍 제어

    - 역전류 차단하여 회로 보호(입출력에 버퍼 장착)

 

2. Tri-state buffer

    - 제어 신호가 입력되면, 버퍼 활성화(Data 출력)

    - 제어 신호없으면, Z(High impedance) 출력

    - bufif0 (출력, 입력, 제어(0)신호)

    - bufif1 (출력, 입력, 제어(1)신호)

 

Tri-state buffer

 

module shift_register_SIPO_s( /// Structure modeling 
    input clk, rst, d, r_en,
    output [3:0] q
);
    wire [3:0] shift_register;
    D_flip_flop_n SP1(.d(d), .clk(clk), .rst(rst), .q(shift_register[3]));
    D_flip_flop_n SP2(.d(shift_register[3]), .clk(clk), .rst(rst), .q(shift_register[2]));
    D_flip_flop_n SP3(.d(shift_register[2]), .clk(clk), .rst(rst), .q(shift_register[1]));
    D_flip_flop_n SP4(.d(shift_register[1]), .clk(clk), .rst(rst), .q(shift_register[0]));
    
    bufif0 (q[0], shift_register[0], ~r_en); /// bufif : 3-state buffer
    bufif0 (q[1], shift_register[1], ~r_en); /// r_en = 0일때, q[] = shift_register[]
    bufif0 (q[2], shift_register[2], ~r_en); /// bufif0 (출력, 입력, 제어신호)
    bufif0 (q[3], shift_register[3], ~r_en);

endmodule

 

module shift_register_SIPO_h( //// Behavior modeling
    input d, clk, rst, r_en,
    output [3:0] q
);
    reg [3:0] register;
    always@(negedge clk or posedge rst) begin
        if(rst) register <= 0;
        else register <= {d,register[3:1]};
            //// 하나씩 밀고 최상위비트부터 받는 시프트 레지스터
    end
    assign q = (r_en) ? register : 4'bzzzz;
    	//// serial로 다 받는 거 기다렸다가,
        //// r_en = 1, register 출력, 아니면 z
endmodule

SI-PO shift register

 

3. 병렬 입력 - 병렬 출력 레지스터

    = PI-PO Register( parallel input - parallel output)

       그냥 Register라고 부른다.

 

 

PPT에 있는 그림 잘못 됨.

WR = 0 을 주면, AND gate 출력 = 0 이라 DFF가 클리어되어버린다.

→ 메모리 다 날라감

∴ AND gate 대신 MUX를 써야 된다.

 

module shift_register_PIPO( /// 4bit PIPO
     input [3:0] d, 
     input clk, rst, w_en, r_en, 
     output q 
); 

    reg [3:0] register; 
    always@(posedge clk, posedge rst) begin 
        if(rst) register = 0; 
        else if(w_en) register = d; /// 4bit짜리 4bit로 받음(PI) 
            else register = register;  
    end 

    assign q = r_en ? register : 'bz; 
     /// 4bit짜리 4bit로 출력함.(PO) 
     /// 'bz : 몇 bit가 되던 z로 다 채워지니 괜찮음. 
endmodule

 

<Parameter 활용>

module shift_register_PIPO #(parameter N=8)(
     input [N-1:0] d,
     input clk, rst, w_en, r_en,
     output q
);
    reg [N-1:0] register;
    always@(posedge clk, posedge rst) begin
        if(rst) register = 0;
        else if(w_en) register = d;
            else register = register; 
    end
    
    assign q = r_en ? register : 'bz;
endmodule

 

4. 범용 시프트 레지스터

     : SISO, SIPO, PISO, PIPO 전부 다 가능하도록 설계된 레지스터

 

블록도는 전원 표시 x, 실제 핀 배치에는 전원 꼭 넣어줘야 한다.

 

module shift_register( /// 범용 시프트 레지스터
    input clk, rst, shift, load, sin,
          /// sin : serial input
    input [7:0] data_in,
          /// data_in : parallel input
    output reg [7:0] data_out
);
    
    always@(posedge clk, posedge rst) begin
        if(rst) data_out = 0;
        else if(shift) data_out = {sin, data_out[7:1]};
                  /// 직렬입력받아서 시프트하고 출력
             else if(load) data_out = data_in;
                  else data_out = data_out;
     end
endmodule

아두이노 키트에 들어있는 범용레지(74HC595)

shift clk 마다 serial input이 착착착 저장되고 LATCH에 신호를 주면 parallel로 출력된다.

 

임베디드를 하다 보면, 포트 부족 문제가 자주 발생하고

이 문제를 해결하기 위해서 범용 레지스터를 사용한다.

포트의 확장(SI 한 포트로 받아서 PO 다중 비트로 출력)

 

[Memory]

 

Memory

: 레지스터를 쭉 배열해놓은 것이 바로 메모리

 

<1 Kbyte static memory>

   (Reg 쭉 이어붙여놓은 것이 static memory)

module sram_8bit_1024(
    input clk, w_en, r_en, /// 메모리는 리셋하지 않으므로 rst 없다.
    input [9:0] addr, /// address(select bit)가 10bit(2^10이니까)
    inout [7:0] data /// inout : 인풋도 되고 아웃풋도 되고 만능
);

    reg [7:0] mem [0:1023]; //// 0~1023까지의 array
    always @(posedge clk)
        if(w_en) mem[addr] <= data; /// else이면 그냥 유지
                                    /// 0번 메모리에 접근하는 것은 mem[0],
                                    /// 여기서 []는 bit가 아닌, 배열을 의미한다.

    assign data = r_en ? mem[addr] : 8'bz;
        /// w_en / r_en 평소에는 0으로 유지하고 있다가 필요할 때만 1 줘서 씀.
    
endmodule

※ 메모리용 회로는 따로 만들어져 있다.

메모리용 회로

FPGA 에서 DFF는 항상 LUT과 붙어 있다.

메모리는 결국 DFF들의 배열로 만들어지는 건데, 그렇게 되면 붙어 있는 LUT들을 낭비하게 되는 격이다.

∴ 따로 만들어져 있는 메모리용 회로를 사용한다.

 

※ Vector와 Array

1. Vector

   : n비트 폭을 가진 하나의 원소를 의미

   - net과 reg 데이터 타입은 여러개의 비트를 가진 벡터로 선언할 수 있다.
      <data type> [msb:lsb] <변수 명> 
   - [높은 수:낮은 수] 또는 [낮은 수:높은 수] 둘 다 나타낼 수 있으며,

      항상 왼쪽에 있는 것이 최상위 비트를 나타낸다.
   - 비트 폭을 따로 선언하지 않으면 1비트 스칼라이다.

   - 보통 width라고 한다.

 

///// 예   시 /////

wire [31:0] data; // 32비트 'data' 선언
wire [7:0] d1, d2, d3; // 3개의 'd1~3'을 각각 8비트로 선언
reg [0:15] count; // 16비트 폭을 가진 reg 'count'

 

2. Array(배열)

   : 1비트 또는 n비트를 가지는 여러 원소를 의미

   - reg, integer, time, 벡터 레지스터 데이터 타입의 배열을 선언할 수 있다.
     <data type> [msb:lsb] <배열 이름> [배열 갯수 -1 : 0]
   - 보통 depth라고 한다.

      cf) 2차원 배열 : < 배열 명 > [행 범위] [열 범위]

 

///// 예   시 /////

integer memory [0:3]; // 4개의 memory 변수의 배열
reg memory [15:0]; // 16개의 memory 레지스터 변수의 배열
reg [5:0] mem [0:9]; // 10개의 mem 변수의 배열, 각 변수는 6비트 폭을 가짐
integer mem [3:0][3:0]; // 4x4 2D array

위와 같이 2차원 사각형을 생각하면 쉽게 이해할 수 있다.

∴ mem[0] <= data : 0번째 배열에 data를 넣어주겠다는 의미

 

 

 

 

Verilog는 잠시 끝,

다음 주부터는 Embedded 시작

 

 

728x90
반응형