[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
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
※ Test bench sequence
1. DUT instance
2. 입력은 reg, 출력은 wire로 선언
3. 초기화
4. clk 생성
5. test 하고싶은 값 넣고 시뮬레이션
3. 직렬 입력 - 병렬 출력 레지스터
= SI-PO Register( serial input - parallel output)
※ Buffer
1. Buffer
- 타이밍 제어
- 역전류 차단하여 회로 보호(입출력에 버퍼 장착)
2. Tri-state buffer
- 제어 신호가 입력되면, 버퍼 활성화(Data 출력)
- 제어 신호없으면, Z(High impedance) 출력
- bufif0 (출력, 입력, 제어(0)신호)
- bufif1 (출력, 입력, 제어(1)신호)
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
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 전부 다 가능하도록 설계된 레지스터
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
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 시작
'# Semiconductor > [Semicon Academy]' 카테고리의 다른 글
[Harman 세미콘 아카데미] 12일차 - Review, 디지털 시계 만들기(교육 과정 외) (0) | 2023.07.04 |
---|---|
[Harman 세미콘 아카데미] 11일차 - 반도체 개요, C언어 개요 및 문법 (0) | 2023.07.03 |
[Harman 세미콘 아카데미] 9일차 - Counter 활용(parameter, bin_to_dec, TFF, edge_detector, debouncing) (0) | 2023.06.29 |
[Harman 세미콘 아카데미] 8일차 - review, async/sync counter (0) | 2023.06.28 |
[Harman 세미콘 아카데미] 6~7일차 - 취업 특강 (0) | 2023.06.26 |