BLOG ARTICLE I-type | 1 ARTICLE FOUND

  1. 2009/02/02 컴퓨터구조 설계 및 실험 - 16bit CPU (I-type 추가)



다운/퍼 가실 때 댓글 부탁드려요. 그리고 받으신 자료 유료 레포트 사이트에 올리지 말아주세요.




1.

. 기존에 구현한 Single Cycle CPU I-type 형식의 instruction 처리하는 기능을 추가한다.

. Instruction Decoder에서 R-type I-type instruction 해석하여 상황에 맞는 controller signal 설정하고 정상적인 동작상태를 보인다.


2.
알고리즘

. Instruction Fetch

1) 클럭에 동기화 되어 PC값이 증가되면서 해당 rom_data 있는 instruction 읽어온다

2) pc 클럭마다 1 증가되고 새로운 instruction 받아오게 된다.(single_cycle 특성)

3) rom_data 수는 개인 64개를 사용한다.

. Decoder

R-type

I-type

1) 받아온 instruction 중에서 R-type 6비트를 I-type 3비트를 명령어로 사용한다.

2) R-type 하위 10비트 각각 5비트씩은 read_address1, read_address2 사용하고, I-type 하위13비트 5비트는 read_address1, 8비트는 immediate 값으로 사용한다.

3) 또한 클록에 동기화되어 write_address [9:5]비트의 데이터를 주소로 사용한다.

. Register & ALU

1) Decoder에서 해석된 operand(명령어) R-type(6비트), I-type(3비트) 모두 adder기능을 수행한다.

2) R-type 경우 read_address1 read_address2주소에 해당하는 read_data1 read_data2 rom_table에서 꺼내서 ALU 연산(adder) 통해서 결과값을 다시 클럭이 posedge 때마다 read_address1(write_address) 위치에 삽입한다. (ex. a = a + b)

3) I-type 경우 read_address1 주소에 해당하는 read_data1 rom_table에서 꺼내고 immediate값을 바로 ALU 연산(adder)하여 결과값을 다시 매 클럭이 posedge일 때마다 read_address1(write_address) 삽입한다. (ex. a = a + constant(10))

3) R-type에서 read_enable1, read_enable2 instruction write_enable값은 클럭에 동기화되어 1 할당되고 이는 클럭마다 2개의 data add 연산하여 레지스터에 쓰는 구조를 갖게됨을 의미한다. I-type의 경우 read_enable2 0으로 되고 이는 read_address2 대신에 immediate 사용함을 의미한다.


3.

module cpu(clk, pc, rst, write_data, read_address1, read_address2, read_enable1, read_enable2);

 

input clk, rst;

 

output [7:0] write_data;

output [5:0] pc;

output [4:0] read_address1, read_address2;

output read_enable1, read_enable2;

 

reg [4:0] read_address1, read_address2, write_address;

reg [5:0] pc;

reg [7:0] read_data1, read_data2;

reg [15:0] inst_out;

reg read_enable1, read_enable2, write_enable;

 

// rom_table 모듈 시행

m_rom_table rom_table(clk, pc, rst, inst_out);

 

// instruction decoder 모듈 시행

m_inst_decoder inst_decoder(clk, inst_out, read_address1, read_address2, write_address, read_enable1, read_enable2, write_enable);

 

// register 모듈 시행

m_register register(clk, rst, read_enable1, read_enable2, write_enable, read_address1, read_address2, write_address, read_data1, read_data2, write_data);

 

// alu 모듈 시행 func_sel result_sel 값은 adder기능을 수행하도록 0 2'b00을 대입

m_alu alu(read_data1, read_data2, 0, 2'b00, write_data);

 

always@(posedge clk) // 클럭마다 pc값을 1 증가

begin

pc = pc + 1'b1;

end

 

endmodule

 

 

 

module m_rom_table(clk, pc, rst, inst_out);

 

input [5:0] pc;

input clk, rst;

 

output [15:0] inst_out;

 

reg [15:0] inst_out;

reg [15:0] rom_data[0:63];

 

always@(posedge clk or negedge rst)

begin

if(rst == 1'b0) // rst 0이면 rom_data 테이블 초기화

begin

rom_data[0] = 16'b0000010000000001;

rom_data[1] = 16'b0000010000000001;

rom_data[2] = 16'b0000010000000001;

rom_data[3] = 16'b0000010000000001;

 

rom_data[4] = 16'b0000010000000001;

rom_data[5] = 16'b0000010000000001;

rom_data[6] = 16'b0000010000000001;

rom_data[7] = 16'b0000010000000001;

rom_data[8] = 16'b0000010000000001;

rom_data[9] = 16'b0000010000000001;

rom_data[10] = 16'b0000010000000001;

rom_data[11] = 16'b0000010000000001;

rom_data[12] = 16'b0000010000000001;

rom_data[13] = 16'b0000010000000001;

rom_data[14] = 16'b0000010000000001;

rom_data[15] = 16'b0000010000000001;

rom_data[16] = 16'b0000010000000001;

rom_data[17] = 16'b0000010000000001;

rom_data[18] = 16'b0000010000000001;

rom_data[19] = 16'b0000010000000001;

rom_data[20] = 16'b0000010000000001;

rom_data[21] = 16'b0000010000000001;

rom_data[22] = 16'b0000010000000001;

rom_data[23] = 16'b0000010000000001;

rom_data[24] = 16'b0000010000000001;

rom_data[25] = 16'b0000010000000001;

rom_data[26] = 16'b0000010000000001;

rom_data[27] = 16'b0000010000000001;

rom_data[28] = 16'b0000010000000001;

rom_data[29] = 16'b0000010000000001;

rom_data[30] = 16'b0000010000000001;

rom_data[31] = 16'b0000010000000001;

rom_data[32] = 16'b0000010000000001;

rom_data[33] = 16'b0000010000000001;

rom_data[34] = 16'b0000010000000001;

rom_data[35] = 16'b0000010000000001;

rom_data[36] = 16'b0000010000000001;

rom_data[37] = 16'b0000010000000001;

rom_data[38] = 16'b0000010000000001;

rom_data[39] = 16'b0000010000000001;

rom_data[40] = 16'b0000010000000001;

rom_data[41] = 16'b0000010000000001;

rom_data[42] = 16'b0000010000000001;

rom_data[43] = 16'b0000010000000001;

rom_data[44] = 16'b0000010000000001;

rom_data[45] = 16'b0000010000000001;

rom_data[46] = 16'b0000010000000001;

rom_data[47] = 16'b0000010000000001;

rom_data[48] = 16'b0000010000000001;

rom_data[49] = 16'b0000010000000001;

rom_data[50] = 16'b0000010000000001;

rom_data[51] = 16'b0000010000000001;

rom_data[52] = 16'b0000010000000001;

rom_data[53] = 16'b0000010000000001;

rom_data[54] = 16'b0000010000000001;

rom_data[55] = 16'b0000010000000001;

rom_data[56] = 16'b0000010000000001;

rom_data[57] = 16'b0000010000000001;

rom_data[58] = 16'b0000010000000001;

rom_data[59] = 16'b0000010000000001;

rom_data[60] = 16'b0000010000000001;

rom_data[61] = 16'b0000010000000001;

rom_data[62] = 16'b0000010000000001;

rom_data[63] = 16'b0000010000000001;

 

end

end

 

always@(posedge clk) // 클럭마다 pc에 해당하는 rom_data의 값 inst_out에 읽어옴

begin

inst_out = rom_data[pc];

end

 

endmodule

 

module m_inst_decoder(clk, inst_in, read_address1, read_address2, write_address, read_enable1, read_enable2, write_enable);

 

input clk;

input [15:0] inst_in;

 

output [4:0] read_address1, read_address2, write_address;

output read_enable1, read_enable2, write_enable;

 

reg [4:0] read_address1, read_address2, write_address;

reg read_enable1, read_enable2, write_enable;

 

always@(inst_in) // instruction을 받을 때마다 read_address1,2를 저장

begin

if(inst_in[15:10] == 6'b000001) // R-type 명령어일 때

begin

read_address1 = inst_in[9:5];

read_address2 = inst_in[4:0];

read_enable1 = 1;

read_enable2 = 1;

end

else if(inst_in[15:13] == 3'b001) // I-type 명령어 일 때

begin

read_address1 = inst_in[12:8];

imm_data = inst_in[7:0]; // read_address2 대신 imm_data값 저장

read_enable1 = 1;

read_enable2 = 0;

end

end

 

always@(posedge clk) // 매 클럭마다 5~9비트 데이터를 write_address로 사용

begin

if(inst_in[15:10] == 6'b000001) // R-type 명령어일 때

write_address = inst_in[9:5];

else if(inst_in[15:13] == 3'b001) // I-type 명령어일 때

write_address = inst_in[12:8];

 

write_enable = 1; // 어느 타입이건 매 클럭마다 write

end

 

endmodule

 

 

 

module m_register(clk, rst, read_enable1, read_enable2, write_enable, read_address1, read_address2, write_address, read_data1, read_data2, write_data);

 

input clk, rst, read_enable1, read_enable2, write_enable;

input [4:0] read_address1, read_address2, write_address;

input [7:0] write_data;

 

output [7:0] read_data1, read_data2;

 

reg [7:0] read_data1, read_data2;

reg [7:0] rom_table[0:31];

 

always@(posedge clk or negedge rst)

begin

if(rst == 1'b0) // rst 0이면 rom_table 초기화

begin

rom_table[0] = 8'b00000000;

rom_table[1] = 8'b00000001;

rom_table[2] = 8'b00000010;

rom_table[3] = 8'b00000011;

rom_table[4] = 8'b00000100;

rom_table[5] = 8'b00000101;

rom_table[6] = 8'b00000110;

rom_table[7] = 8'b00000111;

rom_table[8] = 8'b00001000;

rom_table[9] = 8'b00001001;

rom_table[10] = 8'b00001010;

rom_table[11] = 8'b00001011;

rom_table[12] = 8'b00001100;

 

rom_table[13] = 8'b00001101;

rom_table[14] = 8'b00001110;

rom_table[15] = 8'b00001111;

rom_table[16] = 8'b00010000;

rom_table[17] = 8'b00010001;

rom_table[18] = 8'b00010010;

rom_table[19] = 8'b00010011;

rom_table[20] = 8'b00010100;

rom_table[21] = 8'b00010101;

rom_table[22] = 8'b00010110;

rom_table[23] = 8'b00010111;

rom_table[24] = 8'b00011000;

rom_table[25] = 8'b00011001;

rom_table[26] = 8'b00011010;

rom_table[27] = 8'b00011011;

rom_table[28] = 8'b00011100;

rom_table[29] = 8'b00011101;

rom_table[30] = 8'b00011110;

rom_table[31] = 8'b00011111;

end

else if(write_enable == 1'b1) // write_enable 1이면 레지스터에 데이터 쓰기

rom_table[write_address] = write_data;

end

 

always@(read_address1)

begin

if(read_enable1 == 1'b1) // read_enable1 1이면 레지스터에서 데이터 읽음

read_data1 = rom_table[read_address1];

end

 

always@(read_address2)

begin

if(read_enable2 == 1'b1) // read_enable2 1이면 레지스터에서 데이터 읽음

read_data2 = rom_table[read_address2];

else // 아니면 imm_data를 바로 read_data2에 저장

read_data2 = imm_data;

end

 

endmodule

 

 

 

module m_alu(a, b, func_sel, result_sel, result); // 이 이하부터는 ALU 연산

 

input [7:0] a, b;

input func_sel;

input [1:0] result_sel;

 

output [7:0] result;

 

reg [7:0] result;

reg [7:0] adder_result;

reg [7:0] and_result;

reg [7:0] or_result;

 

m_adder adder1(a, b, adder_result, func_sel);

m_and and1(a, b, and_result);

m_or or1(a, b, or_result);

 

always@(result or adder_result or and_result or or_result or result_sel)

case(result_sel)

2'b00 : result = adder_result;

2'b01 :

begin

if(adder_result >= 0)

result = 8'b00000000;

else

result = 8'b00000001;

end

 

2'b10 : result = and_result;

2'b11 : result = or_result;

endcase

 

endmodule

 

 

 

module m_adder(a, b, adder_result, func_sel);

 

input [7:0] a, b;

input func_sel;

 

output [7:0] adder_result;

 

wire [7:0] c;

wire [7:0] b1;

 

m_complement com1(b, b1, func_sel);

 

m_half_adder ha(a[0], b1[0], c[0], adder_result[0]);

m_full_adder fa1(a[1], b1[1], c[0], c[1], adder_result[1]);

m_full_adder fa2(a[2], b1[2], c[1], c[2], adder_result[2]);

m_full_adder fa3(a[3], b1[3], c[2], c[3], adder_result[3]);

m_full_adder fa4(a[4], b1[4], c[3], c[4], adder_result[4]);

m_full_adder fa5(a[5], b1[5], c[4], c[5], adder_result[5]);

m_full_adder fa6(a[6], b1[6], c[5], c[6], adder_result[6]);

m_full_adder fa7(a[7], b1[7], c[6], c[7], adder_result[7]);

 

endmodule

 

 

 

module m_full_adder(a, b, cin, cout, adder_result);

 

input a, b, cin;

output cout, adder_result;

 

wire c1, c2, s1;

 

m_half_adder ha1(b, cin, c1, s1);

m_half_adder ha2(a, s1, c2, adder_result);

 

assign cout = c1 | c2;

 

endmodule

 

 

 

module m_half_adder(a, b, c, adder_result);

 

input a, b;

output c, adder_result;

 

assign adder_result = a ^ b;

assign c = a & b;

 

endmodule

 

module m_complement(b, b1, func_sel);

 

input [7:0] b;

input func_sel;

output [7:0] b1;

 

reg [7:0] tmp;

 

always@(b or tmp or func_sel)

case(func_sel)

1'b0 : tmp = b;

1'b1 : tmp = ~b + 1;

endcase

 

assign b1 = tmp;

 

endmodule

 

 

 

module m_and(a, b, and_result);

 

input [7:0] a, b;

output [7:0] and_result;

 

assign and_result = a & b;

 

endmodule

 

 

 

module m_or(a, b, or_result);

 

input [7:0] a, b;

output [7:0] or_result;

 

assign or_result = a | b;

 

endmodule


4.
파형 분석

. 그림 (1)에서 보면 rst 0으로 초기화 시켜서 rom_data rom_data[0]부터 rom_data[4]까지는 R-type 형식의 instruction rom_data[5]부터 rom_data[10]까지는 I-type 형식의 instruction, 그리고 rom_data[11]부터 rom_data[63]까지는 다시 R-type instruction으로 만든다. R-type 6비트의 명령어(adder만 사용) 5비트의 rs(source), 5비트의 rt(target) 사용하고, I-type 3비트의 명령어(adder 사용) 5비트의 rs(source), 8비트의 immediate value 사용한다.

또한, rom_table rom_table[0] 0(8‘b00000000)으로 저장하고 차례대로 1‘b1 증가하여 마지막 rom_table[31]에는 8'b00011111 초기화시킨다.

I-type rom_data[5]에는 immediate value 3으로, 이후엔 2 초기화되었다.

. R-type rom_table[0] rom_table[1] 더해서 다시 rom_table[0] 저장하는 형태이므로 그림 (2) 같이 클럭마다 1 증가함을 있다.

. I-type rom_table[0] immediate value 더하므로, I-type 시작하는(, read_enable2 0 되는 부분인) rom_table[5] 경우에 3의 값으로 초기화 되어있었으므로 그림 (3)과 같이 write_data(rom_table[0]) 값이 3 증가하며, 후에는 그림 (4) 같이 2 증가됨을 있다.

. 그리고 다시 R-type 시작되는 그림 (5) 보면 이후부터 다시 wirte_data 1 증가됨을 확인할 있다.

 

참고 문헌

. HDL Chip Design(Doone Publications / Douglas J Smith )


저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License