2024.12.3 수업날
이번에는 저번시간에 이어서 ROM을 만들어보고, Processor에 합쳐서 시뮬레이션으로 결과를 확인해본다.
복습을 해보자면
ALU: 연산기
ACC: 누산기(누적해서 계산한다)
PC: 1씩 증가하거나 점프하는 카운터
MAR: ROM으로부터 데이터를 Adress
MDR: ROM으로부터 해당되는 데이터를 받는다
IR: 명령 레지스터
< ROM 만들기 순서 >
넉넉하게 256byte로 준 것이다.
CE(Chip Enable)을 체크표시한다.
다음으로 아래의 .coe 파일을 다운받는다.
.coe 파일 내용 중
첫 번째 줄은 16진수라는 의미이다.
두 번째 줄은 'cpu 4bit 계산기 프로그램'의 hex code를 차례대로 나열한 것이다.
그리고 아래의 Load COE File에서 다운받은 .coe 파일을 입력하고 OK를 누른다.
그러면 아래와 같이 VHDL 식 코드가 생성된다.
ROM VHDL code
-- (c) Copyright 1995-2024 Xilinx, Inc. All rights reserved.
--
-- This file contains confidential and proprietary information
-- of Xilinx, Inc. and is protected under U.S. and
-- international copyright and other intellectual property
-- laws.
--
-- DISCLAIMER
-- This disclaimer is not a license and does not grant any
-- rights to the materials distributed herewith. Except as
-- otherwise provided in a valid license issued to you by
-- Xilinx, and to the maximum extent permitted by applicable
-- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
-- WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
-- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
-- BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
-- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
-- (2) Xilinx shall not be liable (whether in contract or tort,
-- including negligence, or under any other theory of
-- liability) for any loss or damage of any kind or nature
-- related to, arising under or in connection with these
-- materials, including for any direct, or any indirect,
-- special, incidental, or consequential loss or damage
-- (including loss of data, profits, goodwill, or any type of
-- loss or damage suffered as a result of any action brought
-- by a third party) even if such damage or loss was
-- reasonably foreseeable or Xilinx had been advised of the
-- possibility of the same.
--
-- CRITICAL APPLICATIONS
-- Xilinx products are not designed or intended to be fail-
-- safe, or for use in any application requiring fail-safe
-- performance, such as life-support or safety devices or
-- systems, Class III medical devices, nuclear facilities,
-- applications related to the deployment of airbags, or any
-- other applications that could lead to death, personal
-- injury, or severe property or environmental damage
-- (individually and collectively, "Critical
-- Applications"). Customer assumes the sole risk and
-- liability of any use of Xilinx products in Critical
-- Applications, subject only to applicable laws and
-- regulations governing limitations on product liability.
--
-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
-- PART OF THIS FILE AT ALL TIMES.
--
-- DO NOT MODIFY THIS FILE.
-- IP VLNV: xilinx.com:ip:dist_mem_gen:8.0
-- IP Revision: 13
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
LIBRARY dist_mem_gen_v8_0_13;
USE dist_mem_gen_v8_0_13.dist_mem_gen_v8_0_13;
ENTITY dist_mem_gen_0 IS
PORT (
a : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
qspo_ce : IN STD_LOGIC;
spo : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
);
END dist_mem_gen_0;
ARCHITECTURE dist_mem_gen_0_arch OF dist_mem_gen_0 IS
ATTRIBUTE DowngradeIPIdentifiedWarnings : STRING;
ATTRIBUTE DowngradeIPIdentifiedWarnings OF dist_mem_gen_0_arch: ARCHITECTURE IS "yes";
COMPONENT dist_mem_gen_v8_0_13 IS
GENERIC (
C_FAMILY : STRING;
C_ADDR_WIDTH : INTEGER;
C_DEFAULT_DATA : STRING;
C_DEPTH : INTEGER;
C_HAS_CLK : INTEGER;
C_HAS_D : INTEGER;
C_HAS_DPO : INTEGER;
C_HAS_DPRA : INTEGER;
C_HAS_I_CE : INTEGER;
C_HAS_QDPO : INTEGER;
C_HAS_QDPO_CE : INTEGER;
C_HAS_QDPO_CLK : INTEGER;
C_HAS_QDPO_RST : INTEGER;
C_HAS_QDPO_SRST : INTEGER;
C_HAS_QSPO : INTEGER;
C_HAS_QSPO_CE : INTEGER;
C_HAS_QSPO_RST : INTEGER;
C_HAS_QSPO_SRST : INTEGER;
C_HAS_SPO : INTEGER;
C_HAS_WE : INTEGER;
C_MEM_INIT_FILE : STRING;
C_ELABORATION_DIR : STRING;
C_MEM_TYPE : INTEGER;
C_PIPELINE_STAGES : INTEGER;
C_QCE_JOINED : INTEGER;
C_QUALIFY_WE : INTEGER;
C_READ_MIF : INTEGER;
C_REG_A_D_INPUTS : INTEGER;
C_REG_DPRA_INPUT : INTEGER;
C_SYNC_ENABLE : INTEGER;
C_WIDTH : INTEGER;
C_PARSER_TYPE : INTEGER
);
PORT (
a : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
d : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
dpra : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
clk : IN STD_LOGIC;
we : IN STD_LOGIC;
i_ce : IN STD_LOGIC;
qspo_ce : IN STD_LOGIC;
qdpo_ce : IN STD_LOGIC;
qdpo_clk : IN STD_LOGIC;
qspo_rst : IN STD_LOGIC;
qdpo_rst : IN STD_LOGIC;
qspo_srst : IN STD_LOGIC;
qdpo_srst : IN STD_LOGIC;
spo : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
dpo : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
qspo : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
qdpo : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
);
END COMPONENT dist_mem_gen_v8_0_13;
ATTRIBUTE X_CORE_INFO : STRING;
ATTRIBUTE X_CORE_INFO OF dist_mem_gen_0_arch: ARCHITECTURE IS "dist_mem_gen_v8_0_13,Vivado 2019.2";
ATTRIBUTE CHECK_LICENSE_TYPE : STRING;
ATTRIBUTE CHECK_LICENSE_TYPE OF dist_mem_gen_0_arch : ARCHITECTURE IS "dist_mem_gen_0,dist_mem_gen_v8_0_13,{}";
ATTRIBUTE CORE_GENERATION_INFO : STRING;
ATTRIBUTE CORE_GENERATION_INFO OF dist_mem_gen_0_arch: ARCHITECTURE IS "dist_mem_gen_0,dist_mem_gen_v8_0_13,{x_ipProduct=Vivado 2019.2,x_ipVendor=xilinx.com,x_ipLibrary=ip,x_ipName=dist_mem_gen,x_ipVersion=8.0,x_ipCoreRevision=13,x_ipLanguage=VERILOG,x_ipSimLanguage=MIXED,C_FAMILY=artix7,C_ADDR_WIDTH=8,C_DEFAULT_DATA=0,C_DEPTH=256,C_HAS_CLK=0,C_HAS_D=0,C_HAS_DPO=0,C_HAS_DPRA=0,C_HAS_I_CE=0,C_HAS_QDPO=0,C_HAS_QDPO_CE=0,C_HAS_QDPO_CLK=0,C_HAS_QDPO_RST=0,C_HAS_QDPO_SRST=0,C_HAS_QSPO=0,C_HAS_QSPO_CE=1,C_HAS_QSPO_RST=0,C_HAS_QSPO_SRST=0,C_HAS_SPO=1,C_HAS_WE=0,C_MEM_INIT_" &
"FILE=dist_mem_gen_0.mif,C_ELABORATION_DIR=./,C_MEM_TYPE=0,C_PIPELINE_STAGES=0,C_QCE_JOINED=0,C_QUALIFY_WE=0,C_READ_MIF=1,C_REG_A_D_INPUTS=0,C_REG_DPRA_INPUT=0,C_SYNC_ENABLE=1,C_WIDTH=8,C_PARSER_TYPE=1}";
BEGIN
U0 : dist_mem_gen_v8_0_13
GENERIC MAP (
C_FAMILY => "artix7",
C_ADDR_WIDTH => 8,
C_DEFAULT_DATA => "0",
C_DEPTH => 256,
C_HAS_CLK => 0,
C_HAS_D => 0,
C_HAS_DPO => 0,
C_HAS_DPRA => 0,
C_HAS_I_CE => 0,
C_HAS_QDPO => 0,
C_HAS_QDPO_CE => 0,
C_HAS_QDPO_CLK => 0,
C_HAS_QDPO_RST => 0,
C_HAS_QDPO_SRST => 0,
C_HAS_QSPO => 0,
C_HAS_QSPO_CE => 1,
C_HAS_QSPO_RST => 0,
C_HAS_QSPO_SRST => 0,
C_HAS_SPO => 1,
C_HAS_WE => 0,
C_MEM_INIT_FILE => "dist_mem_gen_0.mif",
C_ELABORATION_DIR => "./",
C_MEM_TYPE => 0,
C_PIPELINE_STAGES => 0,
C_QCE_JOINED => 0,
C_QUALIFY_WE => 0,
C_READ_MIF => 1,
C_REG_A_D_INPUTS => 0,
C_REG_DPRA_INPUT => 0,
C_SYNC_ENABLE => 1,
C_WIDTH => 8,
C_PARSER_TYPE => 1
)
PORT MAP (
a => a,
d => STD_LOGIC_VECTOR(TO_UNSIGNED(0, 8)),
dpra => STD_LOGIC_VECTOR(TO_UNSIGNED(0, 8)),
clk => '0',
we => '0',
i_ce => '1',
qspo_ce => qspo_ce,
qdpo_ce => '1',
qdpo_clk => '0',
qspo_rst => '0',
qdpo_rst => '0',
qspo_srst => '0',
qdpo_srst => '0',
spo => spo
);
END dist_mem_gen_0_arch;
이제 ROM VHDL 코드를 저번에 만들었던 Processor의 코드에 인스턴스 한다.
Processor 최종 코드
module Processor(
input clk, reset_p,
input [3:0] key_value,
input key_valid,
output [3:0] kout, // cpu가 key 입력을 받았는지 확인함
output [7:0] outreg_data);
wire [7:0] int_bus, mar_out, rom_out;
wire mar_inen, mdr_inen; // in enable
wire mdr_oen; // out enable
wire pc_inc, load_pc, pc_oen;
wire [7:0] ir_data;
wire ir_inen;
wire [3:0] bus_reg_data;
wire breg_inen;
wire acc_high_reset_p, acc_oen, acc_in_select;
wire [1:0] acc_high_select_in, acc_low_select;
wire op_add, op_sub, op_mul, op_div, op_and;
wire zero_flag, sign_flag;
wire tmpreg_inen, tmpreg_oen;
wire creg_inen, creg_oen;
wire dreg_inen, dreg_oen;
wire rreg_inen, rreg_oen;
wire outreg_inen;
wire keychreg_oen;
wire inreg_oen;
wire keyout_inen;
// Control_Block 모듈: CPU 제어 논리
// 입력: 클럭, 리셋, IR 데이터, 플래그
// 출력: 여러 컨트롤 신호 (MAR, MDR, PC, ALU 등) 활성화 및 설정 신호
Control_Block CB(
clk, reset_p,
ir_data,
zero_flag, sign_flag,
mar_inen, mdr_inen, mdr_oen, ir_inen, pc_inc, load_pc, pc_oen, breg_inen,
acc_high_reset_p, acc_oen, acc_in_select,
acc_high_select_in, acc_low_select,
op_add, op_sub, op_mul, op_div, op_and,
tmpreg_inen, tmpreg_oen, creg_inen, creg_oen,
dreg_inen, dreg_oen, rreg_inen, rreg_oen,
outreg_inen, keychreg_oen, inreg_oen, keyout_inen, rom_en);
// Program_addr_counter 모듈: Program Counter 제어
// PC 증가 또는 로드 수행, 내부 버스를 통해 값을 전달
Program_addr_counter PC(
.clk(clk), .reset_p(reset_p),
.pc_inc(pc_inc), .load_pc(load_pc), .pc_rd_en(pc_oen),
.pc_in(int_bus),
.pc_out(int_bus));
// MAR (Memory Address Register): 메모리 주소 저장
register_Nbit_p # (.N(8)) MAR(
.clk(clk), .reset_p(reset_p),
.d(int_bus), // 내부 버스에서 입력 받음
.wr_en(mar_inen), .rd_en(1),
.register_data(mar_out)); // 출력 데이터 (MAR의 주소 값)
// MDR (Memory Data Register): 메모리 데이터 저장
register_Nbit_p # (.N(8)) MDR(
.clk(clk), .reset_p(reset_p),
.d(rom_out), // ROM의 출력 데이터를 입력
.wr_en(mdr_inen), .rd_en(mdr_oen),
.q(int_bus)); // 내부 버스에 출력 연결
// IR (Instruction Register): 명령어 저장
register_Nbit_p # (.N(8)) IR(
.clk(clk), .reset_p(reset_p),
.d(int_bus), // 내부 버스에서 입력 받음
.wr_en(ir_inen), .rd_en(1),
.register_data(ir_data)); // 출력 데이터 (명령어)
// BREG: ALU 연산을 위해 데이터 보관 (4비트)
register_Nbit_p # (.N(4)) BREG( // ALU를 4 bit로 사용했기 때문
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]), // 상위 4비트만 입력으로 사용
.wr_en(breg_inen), .rd_en(1),
.register_data(bus_reg_data)); // ALU로 전달할 데이터
// ALU 및 ACC: 산술 및 논리 연산 수행
Block_ALU_ACC b_alu_acc(
.clk(clk), .reset_p(reset_p), .acc_high_reset_p(acc_high_reset_p),
.rd_en(acc_oen), .acc_in_select(acc_in_select),
.acc_high_select_in(acc_high_select_in), .acc_low_select(acc_low_select),
.bus_data(int_bus[7:4]), // ALU 입력 데이터
.op_add(op_add), .op_sub(op_sub), .op_mul(op_mul), .op_div(op_div), .op_and(op_and), // 연산 제어
.bus_reg_data(bus_reg_data), // 레지스터 데이터
.zero_flag(zero_flag), .sign_flag(sign_flag), // 상태 플래그
.acc_data(int_bus)); // 내부 버스로 결과 출력
// TMPREG: 임시 레지스터
register_Nbit_p # (.N(4)) TMPREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]),
.wr_en(tmpreg_inen), .rd_en(tmpreg_oen),
.q(int_bus[7:4]));
// CREG, DREG, RREG: 추가 레지스터 (4비트)
register_Nbit_p # (.N(4)) CREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]),
.wr_en(creg_inen), .rd_en(creg_oen),
.q(int_bus[7:4]));
register_Nbit_p # (.N(4)) DREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]),
.wr_en(dreg_inen), .rd_en(dreg_oen),
.q(int_bus[7:4]));
register_Nbit_p # (.N(4)) RREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]),
.wr_en(rreg_inen), .rd_en(rreg_oen),
.q(int_bus[7:4]));
// OUTREG: CPU 출력용 레지스터
register_Nbit_p # (.N(8)) OUTREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus),
.wr_en(outreg_inen),
.register_data(outreg_data));
// KEYCHREG: 키 입력 유효성을 확인하기 위한 레지스터
register_Nbit_p # (.N(4)) KEYCHREG(
.clk(clk), .reset_p(reset_p),
.d({key_valid, key_valid, key_valid, key_valid}),
.wr_en(1), .rd_en(keychreg_oen),
.q(int_bus[7:4]));
// INREG: 키 값을 저장하는 레지스터
register_Nbit_p # (.N(4)) INREG(
.clk(clk), .reset_p(reset_p),
.d(key_value),
.wr_en(1), .rd_en(inreg_oen),
.q(int_bus[7:4]));
// KEYOUTREG: 키 출력 확인 레지스터
register_Nbit_p # (.N(4)) KEYOUTREG(
.clk(clk), .reset_p(reset_p),
.d(int_bus[7:4]),
.wr_en(keyout_inen), .rd_en(1),
.register_data(kout));
/////////// 2024.12.03 --> ROM 추가
dist_mem_gen_0 rom(
.a(mar_out),
.qspo_ce(rom_en),
.spo(rom_out));
endmodule
< Processor simulation >
다음으로, Test bench를 통해 Processor에서 사칙연산이 잘 되는지 확인한다.
1. 덧셈
Test bench Processor code
module tb_Processor();
reg clk, reset_p;
reg [3:0] key_value;
reg key_valid;
wire [3:0] kout;
wire [7:0] outreg_data;
Processor DUT(
clk, reset_p,
key_value,
key_valid,
kout, // cpu가 key 입력을 받았는지 확인함
outreg_data);
initial begin
clk = 0; reset_p = 1;
key_value = 0;
key_valid = 0;
end
always #5 clk = ~clk;
initial begin
#10; reset_p = 0; #10; // 초기화 끝
key_value = 3; key_valid = 1; #10_000; // 10us
key_value = 0; key_valid = 0; #10_000;
key_value = 4'ha; key_valid = 1; #10_000; // a --> '+'
key_value = 0; key_valid = 0; #10_000;
key_value = 5; key_valid = 1; #10_000;
key_value = 0; key_valid = 0; #10_000;
key_value = 4'hf; key_valid = 1; #10_000; // f --> '='
key_value = 0; key_valid = 0; #10_000;
$stop;
end
endmodule
결과를 보기 위해서는 시뮬레이션 창에서 상단의 1ms를 실행해야한다.
그리고 key가 눌렸을 때만 key_valid가 1이 되는 것이다.
위의 코드는 덧셈일때를 나타낸 것인데,
3 a 5 f 08 이렇게 된 경우는 --> 3 + 5 = 8이라는 의미이다.
다음으로 십의 자리가 증가하는 덧셈의 경우를 알아보자.
7 a 8 f 0f 이렇게 된 경우는 --> 7 + 8 = 15 라는 의미이다.
원래는 16진수로 결과값이 나타나있는데, Radix를 Unsigned Decimal로 변경하면 10진수로 값을 확인할 수 있다.
위의 코드에서 사용하였듯이 a~f는 아래와 같은 의미의 연산이다.
a(10): +
b(11): -
c(12): and연산
d(13): /
e(14): *
f(15): =
2. 뺄셈
다음으로 뺄셈일 때를 살펴보자.
9 b 5 f 04 이렇게 된 경우는 --> 9 - 5 = 4 라는 의미이다.
3. 곱셈
다음으로 곱셈일 때를 살펴보자.
7 e 8 f 38 이렇게 된 경우는 --> 7 x 8 = 56 라는 의미이다.
원래는 16진수로 결과값이 나타나있는데, Radix를 Unsigned Decimal로 변경하면 10진수로 값을 확인할 수 있다.
4. 나눗셈
다음으로 나눗셈일 때를 살펴보자.
8 d 5 f 01 03 이렇게 된 경우는 --> 8 / 5 = 1(몫) 3(나머지) 라는 의미이다.
cpu의 구조와 동작원리5(ROM, Processor simulation) 끝!