SingleCycleCPU(verilog)

2022. 12. 24. 12:12기록지/컴퓨터구조(computer architecture)

SingleCycleCpu

명령이 32bit로 들어와 명령에 따라서 파싱(Parsing)되어 CPU를 한

바퀴 돌며 명령을 수행하도록 하는 CPU를 직접 구현하도록 한다.

순히 구현할 수 있다는 장점이 있지만 한 명령어 당 1번의 사이클만

이 할당되므로 다른 모듈이 움직이고 있을 때 다른 모듈은 IDLE 상태

가 될 수 있다. 이런 Single Cycle CPU를 구현함으로써 Memory

Register와 같은 CPU의 구성요소가 하는 역할에 대해 배우고 구현 할

수 있다. 또한 플립플롭, PC 등과 같은 기본적인 요소들이 실질적으

로 어떻게 사용되는 방법에 대해 알 수 있다.

 

설계세부사항

가장 먼저 PC를 보면 말 그대로 Program Counter의 역할을 한다.

즉 다음 access 할 주소의 값을 Instruction Memory로 넣어주도록 하는데 이 값은 Idle 할 때는 +4 씩 더해주어 다음 주소를 가리키도록 하며  만약 jump가 들어왔을 때는 (ALU를 거치지 않고) 현재 PC + 4 + jump값을 연산해주어 PCjump된 값을 가리키도록 해준다또 하나 PC가 하는 역할은 Branch가 된 값을 jump와 같이 PC에 넣어줄 수 있다는 것이다.

기본적으로 PCI_clk(clock)의 값이 상승엣지 일 때 PC의 값이 ADDERInstruction Memory로 값이 전달되게 된다. 위에서 언급한 Idle한 상황으로 값이 Adder에 전달되어 4씩 더해지며 다음 값을 IM에 넣어 IM이 다음 연산을 할 수 있도록 돕는다.

 

다음으로 jump 상황이다. 먼저 알아두어야 할 사항은 PC에서 전달해준 주소 값에 따라 j값을 계산하는지 하지 않는지를 결정하는 것이 아니라 일단은 모두 계산한 후에 jump와 연결된 mux에서 어떤 값을 PC의 다음 값으로 결정할지를 선택하게 된다. 그러므로 일단은 Instruction Memory에서 나온 immediate(추측하고) 값을 2LS(Left Shift)하여 28비트로 만들고 이 값을 현재 PC값에 + 4 된 값을 concatenation시킨다. pc(31-28), immediate(27-0) 이렇게 나온 값이 Control에 의해 jump instruction이라 판명되면 mux에서 jump한 값을 PC의 다음 값으로 넣어주게 된다.

 

 

(표시된 부분이 Branch 부분)

 

PC의 마지막 stateBranch를 보게 되면 일단 PCCLK에 따라서 값을 ADDERIM으로 보내주게 되는데 IM은 타입에 따라서 값을 나눠주게 된다.

 

BranchI타입이므로 rsrt에 값을 넣어주고 op에 연산자를 넣어준다. 가장 먼저 설명할 것은 16비트의 immediate 값이 들어가 sign extention되어 32비트가 되고 이 값을 2비트 만큼 LS시키게 되는 것이다. 그리고는 ALU에서 rsrt의 값을 subtract 해주는데 이 결과 값을 and에 전달하도록 한다. 그래서 Branch 값인 1and 시켜 이 값이 1일 경우에 PC + 4 + Branch의 주소 값을 더한 값을 PC에 넣어주도록 한다.  (subtract 한 값이 0이라면 두 값이 같다는 의미이니 beq의 조건에 부합합니다. (Branch Equal))

PC 이외의 구동방식을 보자면..... PC에서 전달 받은 값에 따른 명령을 IM에서 불러오고 이 값의 타입에 따라서 데이터를 Parsing하게 된다. 이렇게 Parsing 된 값은 ControlRegister 그리고 Sign extend로 연결되는 immediate 전달 값으로 연결되어 값을 전달하게 된다. ALU에서 연산이 끝나게 되면 DM (Data Memory)으로 값이 전달되게 되는데 여기에서 값을 저장할 것인지 아니면 읽을 것인지를 결정한다. (사실 중요한 것은 write입니다. readwrite 모두 인가가 나게 되면 collision이 발생하므로 둘 중 하나만 인가하도록 합니다.) 그리고 Control에서 MemtoReg의 값이 mux1이 들어오게 되면 읽어온 값을 다시 레지스터로 할당하여 준다.

 

 

 

Control을 보자면

RegDstR타입과 I타입에 따라서 rd에 값을 넣어줄지 rt에 값을 넣어줄지를 선택하는 mux에 값을 넣어주는 Control의 명령이다.

 

Control 값에서 RegDst의 값을 주게 되면 mux에서는

RegDst의 값에 따라서 R타입의 rd의 값이 들어가느냐

아니면 I타입의 rt의 값이 들어가느냐를 선택할 수 있다.

 

 

 

ALUop의 값에 따라서 R타입의 functALU control에 넣을지 아니면 opcode를 넣을지를 결정한다. (직접적으로 ALU의 연산이 add인지 sub인지를 결정하는 부분을 어디서 가져다 쓸지를 결정합니다.)

ALUop1이 될 때는 op값에 의해서 ALU의 연산자가 결정된다. (주황색이 I타입 파란색이 R타입입니다.)

예를 들어보면 R타입은 op코드가 아닌 funct의 값에 의해 연산자가 결정된다. (funct에 연산자에 할당된 2진수 값이 들어간다.) 

 

반면에 I타입은 연산자에 할당된 2진수의 값이 op코드로 들어와 어떤 연산을 할지 ALUcontrol로 전달해준다.

그러므로 R타입과 I타입에 따라서 기계어가 적힌 메모장에서 어떤 값을 불러올지를 결정하는 것이 ALUopALUcontrol이 하는 일이다.

 

 

 

R타입과 다르게 I타입은 immediate 값을 가지고 연산을 한다. 그런데 명령어의 길이는 32비트가 최대이므로 32비트 안에 immediate 값을 넣어야 한다. 그게 바로 I타입인데 I타입은 하위 16비트의 값에 2진 값이 들어가게 된다. 물론 넣기는 했지만 이 값을 32비트 값을 갖는 레지스터와 바로 연산할 수 없기 때문에 32비트로 만들어 준다. 예를 들자면...

 

이런 2진수가 immediate 값으로 들어왔다 치자 그렇다면 이 값을 sign extend 시키면 다음과 같은 값이 된다.

 

 

         

즉 값은 변화가 없고 앞에 0으로 16자리를 추가한 것이다.

이렇게 sign extend가 되었다면 MIPS의 레지스터와 연산이 가능해진다.

(나중에 말하겠지만 이 값이 LS(Left Shift) 2되어 PC의 값과 연산되어 질 수 있다.)

 

 

 

IM(Instruction Memory)에 대해 알아보면.

어셈블리에서 기계어로 바꾼 값을 저장한 메모장에서 값을 불러오는 역할을 한다. 이렇게 불러온 값을

다음과 같이 분할하여 넣어준다.

(다시 한 번 언급하지만 구현한 Single Cycle CPU는 조건에 따라 어떤 값을 구하게되는 것이 아니라 이미 대입된 값을 mux나 조건에 따라서 선택하게 됩니다.)

32비트의 Instruction Address가 들어오면 분할되어 32비트의 o_instr로 나가게 된다. *//* 수정

PC의 값은 4씩 더하여 IM으로 들어오게 되는데 그것은

메모장에서 32비트의 값을 불러와 저장한 값이 4이기 때문이다. 그러므로

4씩 불러온다는 것은 Line by Line으로 값을 불러온다는 의미가 된다.

 

 

검은색은 I타입일 경우의 연산이다. (I타입으로 여기는 sw, lwimmediate 주소값만큼 add된 주소를 access 하는 것이므로 add를 사용하게 됩니다. 또 다른 I타입인 beq의 경우는 입력된 2개의 인자를 뺀 값이 0이 될 경우 점프하는 것이므로 sub연산을 합니다. 이 뿐만 아니라 immediate 값을 연산하는 값들이 add, sub로 정의되어 질 수 있습니다.)

녹색으로 표시한 부분은 R타입의 funct값이 들어오는 경로이다. R타입의 funct값이 들어오게 되면 6비트 값에 따라 설정된 연산을 할당하여 ALU로 보내주게 된다. ALU control의 경우 값을 보면

ALU의 경우 어떤 연산을 할지를 ALU control로부터 받아오는데 이 값은 다음과 같이 결정되어진다.

 

Main Control에 의해서 들어온 ALUop에 따라서 ALU control은 다음 과 같은 값을 ALU로 전달하여 준다. Main Control에 의해 들어온 ALUop의 값이 10일 경우에는

R타입이므로 하위 6비트의 값(funct)을 읽어와 할당된 명령으로 ALU로 보내준다.

 

 

 

ALU를 보자면 연산할 값 두 개를 받아 ALU control에 의해서 연산하도록 한다.

ALU를 보게 되면 mux에서 immediate 값과 레지스터 값 둘 중 하나를 선택하여 operandALU에 선택하여 넣어줄 수 있는 역할을 하고 있다. R타입인지 아니면 I타입인지를 판단하여 어떤 값이 들어갈지를 결정하는 mux이다. 이 권한은 Main control이 보내주는 ALUsrc 값에 따라서 선택할 수 있게 된다.

 

 

 

ALU control에서 ALUop 값을 4비트로 받아와 연산을 하도록 한다. data1은 레지스터 값이 오게 되고 data2ALUsrc에 의해서 immediate값 아니면 레지스터를 불러올 수 있다. ALUop 값에 따른 instruction은 다음과 같다.

각 값에 따른 명령을 연산한 값은 이제 DM으로 가게 된다. (o_zero의 값은 and되어 브랜치를 결정하는 mux의 값으로 들어간다고 위에서 언급했기 때문에 따로 적지 않았습니다.)

 

 

 

DM(Data Memory는 값을 저장하는 역할을 하는 모듈입니다.) DMmain control로부터 MemWriteMemRead 값 둘 중 하나만을 받을 수 있다. (읽으며 동시에 쓰게 되면 읽는 값이 변경될 수 있기 때문입니다.)

 

 

먼저 입력 값으로는 I_addrI_data가 들어오게 되는데 I_addr은 주소 값으로 read하거나 write할 값의 주소에 대한 값이다.

다음으로 I_dataMemWrite의 값이 1로 들어올 경우에 I_addr에 저장할 데이터 값을 나타낸다.

값을 읽어왔을 경우에만 o_data32비트 주소 값을 내보내주게 되는데

 

 

DM에 연결된 mux에서는 dataregisterwrite back 하거나 ALU에서 들어온 주소 값을 registerwrite back 할 수 있다. 이 신호 또한  Main Control에서 값을 받아와 mux에서 선택하여 주도록 한다.

(lw, sw에 의해서 값이 불려지거나 저장하여 레지스터로 write back시켜 다른 값을 연산할 수 있도록 합니다.)

 

 

 

마지막으로 register를 보면 CPU 안에 존재하여 소량의 데이터나 처리중인 중간 결과를 일시적으로 기억해 두는 고속 메모리를 register라 한다고 알고 있는데, 이것을 register에 적용을 해보면 주소나 데이터 값을 받아와 ALU등으로 값을  전달하는 버퍼라는 생각이 들었다. 이것을 써보면

IM에 의해서 Parsing 된 각 값을 인자로 read register 1,2 그리고 write register로 불러온다.

 

 

728x90