fivedots.coe.psu.ac.thfivedots.coe.psu.ac.th/software.coe/241-440/csd/csd_1/…  · web...

45
กกกกกกกกก CPU โโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 1. PC โโโโโโโโโโโ Address โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโ 2. Instruction Memory โโโโโ Address โโโ PC โโโโโโโโโโโโโ โ โโโโโโโโ address โโโโโโโโโโโโ 3. Register file โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Instruction โโโโโโโโโโโโโโโ register โโโโโโโโโ 4. ALU โโโโโโโโโโโ Register โโโโโโโโโโโโโโโโโโโโโโโ Output โโโโโ

Upload: others

Post on 17-Apr-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

การออกแบบ CPU

การออกแบบ CPU

โมดูลต่างๆ จะทำงานประสานกันตามลำดับคือ

1. PC จะคำนวณค่า Address ของคำสั่งที่ต้องการทำในขณะนั้น

2. Instruction Memory จะเอา Address จาก PC และนำคำสั่ง ณ ตำแหน่ง address นั้นส่งออกไป

3. Register file จะรับตำแหน่งคือค่าที่ตัดมาจาก Instruction และส่งค่าของตัว register นั้นออกไป

4. ALU จะเอาค่าจาก Register มาทำการคำนวณและส่งค่า Output ออกไป

5. หากคำสั่งนั้นมีการทำงานเกี่ยวกับ Memory ก็จะเข้าไปทำงานในส่วนของ Data Memory

6. สุดท้ายค่าที่คำนวณได้ก็จะถูกเก็บลง Register

Module PC (Program Counter)

โมดูลนี้เป็นโมดูลที่กำหนดตำแหน่งของโปรแกรมในการทำงานโดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(PCClkzrwrstData_in[31:0]addr[31:0])

Input มีทั้งหมด 4 ตัว คือ

1. clk ขนาด 1 bit กำหนดสัญญาณ clock

1. rst ขนาด 1 bit กำหนดสัญญาณ reset

1. rw ขนาด 1 bit กำหนดการทำงานเกี่ยวกับคำสั่ง branch

1. data_in ขนาด 32 bit รับค่าข้อมูลเพื่อเปลี่ยนตำแหน่งโปรแกรม

Output มี 1 ตัว คือ

1. addr ขนาด 32 bit ใช้ส่งค่าตำแหน่งโปรแกรมไปยังโมดูล mem_ins

ในส่วนการทำงาน จะกำหนดการเริ่มต้นของระบบ ซึ่งถูกควบคุม โดย input และให้ output ต่อไปนี้

· ตัวแปร clk เป็นการให้สัญญาณนาฬิกาแก่โมดูล เพื่อเป็นการกำหนดจังหวะในการทำงาน

· ตัวแปร rw คือ input ซึ่งจะกำหนดว่าจะเพิ่มตำแหน่งโปรแกรมอย่างไรโดยมีค่าดังนี้

· ค่า 0 จะหมายถึงให้เพิ่มตำแหน่งของโปรแกรมครั้งละ 1

· ค่า 1 จะหมายถึงให้ทำการเพิ่มตำแหน่งของโปรแกรมไปเท่ากับค่าของ data_in ซึ่งใช้ในคำสั่ง branch และ Jump

· ตัวแปร rst คือ input ซึ่งทำหน้าที่ตั้งค่าใหม่ของระบบ

· ค่า 0 จะหมายถึง ให้ทำการ เริ่มต้นนับใหม่

· ค่า 1 จะหมายถึงให้ทำงานแบบปกติคือเพิ่มค่า address ครั้งละ 1

· ตันแปร data_in คือ ค่าของ address ซึ่ง จะถูกส่งมาจากคำสั่ง branch และ Jump

· ตัวแปร addr คือ ตำแหน่งโปรแกรมที่จะส่งต่อไปยังโมดูล mem_ins

Module Mem_ins (Memory Instruction)

โมดูลนี้จะเป็นส่วนของ memory ที่จัดเก็บคำสั่งโดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(Mem_insinsrw[31:0]addr[31:0])

Input มีทั้งหมด 2 ตัว คือ

1. addr ขนาด 32 bit ใช้รับค่าตำแหน่งโปรแกรมจากโมดูล PC

1. rw ขนาด 1 bit กำหนดการทำงานเกี่ยวกับอ่านและเขียน

Output มี 1 ตัว คือ

1. ins ขนาด 32 bit ใช้ส่งค่าชุดคำสั่งและข้อมูลไปยังโมดูล register

ในส่วนการทำงานจะอ่าน Instruction ที่ตำแหน่งแอดเดรส addr แล้วส่งค่า Instruction ออกไปยังโมดูลอื่นซึ่งถูกควบคุม โดย input และให้ output ดังต่อไปนี้

· ตัวแปร addr คือ input รับค่าตำแหน่งโปรแกรมจากโมดูล PC เพื่อใช้ในการอ่าน Instruction

· ตัวแปร rw คือ input ซึ่งบอกว่าระบบต้องอการอ่านหรือเขียนข้อมูล

· ค่า 0 จะหมายถึง อ่าน Instruction จาก memory_instruction

· ค่า 1 จะหมายถึง เขียนข้อมูลลง memory_instruction ซึ่งส่วนนี้ยังไม่ได้ใช้งาน

· ตัวแปร ins คือ instruction ข้อมูลซึ่งเตรียมส่งต่อไปยังโมดูล register

Module Register

โมดูลนี้จะเป็นที่เก็บ Register ขนาด 32 bit เพื่อใช้ในการคำนวณโดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(REGISTERData_inS2Addr_S1Addr_desAddr_S2rw[31:0][4:0][31:0]S1[4:0][4:0])

Input มีทั้งหมด 5 ตัว คือ

1. addr_s1 ขนาด 5 bit เก็บค่า address ของ operand ตัวที่ 1

2. addr_s2 ขนาด 5 bit เก็บค่า address ของ operand ตัวที่ 2

3. addr_des ขนาด 5 bit เก็บค่า address ของผลลัพธ์ปลายทาง

4. data_in ขนาด 31 bit ใช้รับค่าจากโมดูล data_memory เพื่อเขียนใน register

5. rw ขนาด 1 bit ใช้ควบคุมการทำงานของโมดูล register

Output มี 2 ตัว คือ

1. s1 ขนาด 32 bit ใช้ส่งค่า operand ตัวที่ 1 ให้กับ ALU

2. s2 ขนาด 32 bit ใช้ส่งค่า operand ตัวที่ 2 ให้กับ ALU

ในส่วนการทำงาน จะมี 2 สถานะซึ่งควบคุม โดย rw ดังนี้

· ค่า rw = 0 จะหมายถึงให้ทำการอ่านข้อมูลที่แอดเดรส addr_s1 และ addr_s2 และส่งค่าที่อ่านได้ ( s1 และ s2) ออกไปยัง ALU

· ค่า rw = 1 จะหมายถึงให้ทำการเขียนข้อมูลจาก data_in ไปเก็บที่แอดเดรส addr_des ของรีจิสเตอร์

Module Data_mem (Data Memory)

โมดูลนี้จะเป็นส่วนของ memory ที่สามารถจะอ่านและเขียนได้ ซืงจะสามารถเข้าถึงข้อมูลได้ 2 แบบคือ wordและ byte โดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(Data_memrwbwData_in[31:0]Data_out[31:0]addr)

Input มีทั้งหมด 4 ตัว คือ

1. rw ขนาด 1 bit ใช้กำหนดการอ่านละเขียนข้อมูลลงหน่วยความจำ

1. bw ขนาด 1 bit ใช้กำหนดการเข้าถึงข้อมูล

1. addr ขนาด 32 bit ใช้รับค่าตำแหน่งข้อมูล

1. data_in ขนาด 32 bit ใช้รับค่าข้อมูลเข้า

Output มี 1 ตัว คือ

1. data_out ขนาด 32 bit ใช้ส่งค่าข้อมูล

ในส่วนการทำงาน จะเป็นการโหลดและเขียนค่าจาก memory ซึ่งถูกควบคุม โดย input และให้ output ต่อไปนี้

· ตัวแปร rw คือ input ซึ่งใช้บอกว่าขณะนี้สถานะของระบบอยู่ใน สถานะอ่านหรือเขียน

· ค่า 0 จะหมายถึงระบบกำลังทำคำสั่งเกี่ยวกับการอ่านข้อมูลโดยให้อ่านค่าใน memory และส่งออก ไปยังโมดูลอื่นต่อไป

· ค่า 1 จะหมายถึงระบบจะทำการเขียนข้อมูลลง memory

· ตัวแปร bw คือ input ซึ่งใช้เป็นหน่อยในการเข้าถึงข้อมูล

· ค่า 0 จะหมายถึงระบบต้องการเข้าถึงข้อมูล แบบ byte

· ค่า 1 จะหมายถึงระบบต้องการเข้าถึงข้อมูล แบบ word

· ตัวแปร data_out คือ output ซึ่งรับค่ามาจาก memory โดยจะส่งต่อไปยังโมดูลอื่นต่อไป

Module ALU (Arithmetic and Logic Unit)

โมดูลนี้จะทำหน้าที่ในการคำนวณทางคณิตศาสตร์ โดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(ALUS1Data_outImmedS2Sel2Sel1[31:0][4:0][31:0][31:0]flag)

Input มีทั้งหมด 5 ตัว คือ

1. s1 ขนาด 32 bit ทำหน้าที่รับ operand ชุดที่ 1 จาก register

2. s2 ขนาด 32 bit ทำหน้าที่รับ operand ชุดที่ 2 จาก register

3. immed ขนาด 16 bit ทำหน้าที่รับค่าคงที่ (immediate) จากโมดูล mem_ins

4. sel ขนาด 5 bit ทำหน้าที่รับ opcode จากโมดูล mem_ins

5. sel2 ขนาด 1 bit ทำหน้าที่เป็นตัวเลือก operand ตัวที่ 2 โดยเลือกระหว่าง s2 และ immediate

Output มี 2 ตัว คือ

1. data_out ขนาด 32 bit ใช้ส่งค่าผลลัพธ์ที่ได้จากการคำคำนวณที่ ALU

2. flags ขนาด 1 bit ใช้ส่งค่าทดสอบของ s1 และ s2

ในส่วนการทำงาน sel จะนำ opcode มาทำการ decode เพื่อให้ทราบว่า ALU ต้องทำอะไรโดยจะมีการทำงานทั้งหมดดังนี้

Sel (opcode)

คำสั่ง

Sel2

การกระทำที่ ALU

5’b00000

ADD

-

บวกค่า s1 และ s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b00001

ADDI

-

บวกค่า s1 และค่า immediate แล้วส่งผลลัพธ์ผ่าน data_out

5’b00010

SUB

-

ลบค่า s1 ด้วย s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b00011

OR

-

นำค่า s1 มา OR กับ s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b00100

XOR

-

นำค่า s1 มา XOR กับ s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b00101

SHL

-

Shift ค่า s1 ไปทางซ้ายเท่ากับ s2 bit

5’b00110

SHR

-

Shift ค่า s1 ไปทางขวาเท่ากับ s2 bit

5’b00111

AND

-

นำค่า s1 มา AND กับ s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b01000, 5’b01001

LD(B,W)

0

นำค่า immediate ผ่าน data_out

1

นำค่า s2 ผ่าน data_out

5’b01010, 5’b01011

ST(B,W)

0

นำค่า immediate ผ่าน data_out

1

นำค่า s2 ผ่าน data_out

5’b01100

JMP

-

ไม่มีการกระทำที่ ALU

5’b01101

BEQ

-

ตรวจสอบค่า s1 และ s2 ถ้ามีค่าเท่ากันจะเซ็ต flag เป็น 1

5’b01110

BNEQ

-

ตรวจสอบค่า s1 และ s2 ถ้ามีค่าไม่เท่ากันจะเซ็ต flag เป็น 1

5’b01111

MUL

-

นำค่า s1 คูณกับ s2 แล้วส่งผลลัพธ์ผ่าน data_out

5’b11111

NOP

-

ไม่มีการกระทำที่ ALU

Module CPU

โมดูลนี้จะเป็นโมดูลรวมของทั้งหมดโดยจะมีอินพุตและเอาต์พุตดังต่อไปนี้

(CPUclkrst)

ในโมดูลนี้จะเป็นการรวมโมดูลทั้งหมดเข้าด้วยกันซึ่งจะได้เป็น module CPU ออกมาโดยจะมี Input มีทั้งหมด 2 ตัว คือ

1. clk ขนาด 1 bit

1. rst ขนาด 1 bit

ในส่วนการทำงาน จะเป็น Top hierarchy ของวงจรทั้งหมด ซึ่งถูกควบคุม โดย input

· ตัวแปร clk คือ input ที่ใช้สำหรับกำหนดจังหวะการทำงานของวงจร

· ตัวแปร rst คือ input ที่ใช้สำหรับตั้งค่าเริ่มต้นระบบใหม่ทั้งระบบ

โดยในรายละเอียดของ Code จะทำการรวมโมดูลทั้งหมดโดยต่อโมดูลเข้าด้วยกัน ดังนี้

PC pc1(addr,clk2,rst,rw_PC,data_in_PC);

mem_ins mem1(ins,addr,rw_mem);

register regis1(s1,s2,addr_des,addr_s1,addr_s2,rw_register,data_in);

ALU alu1(data_out1,flag,s1,s2,ins[16:1],ins[31:27],ins[0]);

data_mem mem2(data_out2,rw_memory,bw_memory,data_out1,s1);

โดยทำงานภายใต้ Stage ซึ่งใช้ในตัวแปร count ในการควบคุมโมดูลต่างๆของระบบ ดังต่อไปนี้

· เมื่อ count มีค่าเป็น 0 คือช่วงที่ โมดูล PC เริ่มต้นระบบ

· ป้อนสัญญาณขา rw ค่า 0 ไปยัง โมดูล register

· ป้อนสัญญาณขา rw ค่า 0 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา bw ค่า 1 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา sel ซึ่งใช้ในคำสั่งที่เกี่ยวกับ load และ store เป็น 0

· เมื่อ count มีค่าเป็น 1 คือช่วงที่ ทำการอ่าน ข้อมูลจาก register

· ป้อนสัญญาณขา rw ค่า 0 ไปยัง โมดูล register

· ป้อนสัญญาณขา rw ค่า 0 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา rw ค่า 0 ไปยังโมดูล PC

· ป้อนสัญญาณขา bw ค่า 1 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา sel เป็น 0 ซึ่งใช้สำหรับเลือก data_in ไปยังโมดูล data_mem

· เมื่อ count มีค่าเป็น 2 คือช่วงที่ทำการกำหนด address ของ register S1 ,S2,Des

· ป้อนสัญญาณขา rw ค่า 0 ไปยัง โมดูล register

· ป้อนสัญญาณขา rw ค่า 0 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา rw ค่า 0 ไปยังโมดูล PC

· ป้อนสัญญาณขา bw ค่า 1 ไปยังโมดูล data_mem

· ป้อนสัญญาณขา sel เป็น 0 ซึ่งใช้สำหรับเลือก data_in ไปยังโมดูล data_mem

ใน Stage นี้ Addr_S1 ,Addr_S2 and Addr_Des จะถูกป้อนค่า โดย อ้างอิงค่าจาก Instruction Format ตามที่ได้กำหนดไว้

ตัวอย่าง default:

begin

addr_des = ins[26:22];

addr_s1 = ins[21:17];

addr_s2 = ins[16:12];

end

เป็นการระบุว่า addr_des มีค่าของช่วงตั้งแต่ bit ที่ 22 ถึง bit ที่ 26 ขนาด 5 bit

addr_s1 มีค่าของช่วงตั้งแต่ bit ที่ 17 ถึง bit ที่ 27 ขนาด 5 bit

addr_s2 มีค่าของช่วงตั้งแต่ bit ที่ 12 ถึง bit ที่ 16 ขนาด 5 bit

· เมื่อ count มีค่าเป็น 3 คือช่วงที่ ทำงานสัมพันธ์ระหว่าง register และ memory โดยแต่ละคำสั่งจะทำงานต่างกัน แบ่งเป็นกลุ่มดังต่อไปนี้

1. ใช้งานเฉพาะโมดูล register

5'b00000: begin// ADD

rw_register = 1;

end

5'b00001: begin// ADDI

rw_register = 1;

end

5'b00010: begin// SUB

rw_register = 1;

end

5'b00011: begin// OR

rw_register = 1;

end

5'b00100: begin// XOR

rw_register = 1;

end

5'b00101: begin// SHL

rw_register = 1;

end

5'b00110: begin// SHR

rw_register = 1;

end

5'b00111: begin// AND

rw_register = 1;

end

เป็นคำสั่งที่ใช้งานเฉพาะ register เพียงอย่างเดียว โดยในที่นี้สัญญาณ rw_register ถูกป้อนค่าเป็น 1 นั่นหมายความว่า เป็นการเขียนค่าลง register

1. ใช้งานโมดูล register ร่วมกับ data_mem

5'b01010: begin// STB

rw_register = 0;

rw_memory = 1;

bw_memory = 0;

sel = 1;

end

5'b01011: begin// STW

rw_register = 0;

rw_memory = 1;

bw_memory = 1;

sel = 1;

end

เป็นคำสั่งที่ใช้งานร่วมกันระหว่าง register กับ data_mem ซึ่งกับหน้าที่ ในการ เก็บข้อมูลจาก register มาเก็บไว้ใน memory โดย rw_resgister จะป้อนค่าเป็น 0 เพื่ออ่านข้อมูล และ rw_memory จะป้อนค่าเป็น 1 เพื่อเขียนข้อมูล

1. ใช้งานโมดูลเฉพาะ PC

5'b01100: begin// JMP

rw_PC = 1;

data_in_PC = {8'b0000_0000,ins[26:3]};

end

5'b01101: begin// BEQ

if(flag == 1)

rw_PC = 1;

else if(flag == 0)

rw_PC = 0;

data_in_PC = {16'b0000_0000_0000_0000,ins[16:1]};

end

5'b01110: begin// BNEQ

if(flag == 1)

rw_PC = 1;

else if(flag == 0)

rw_PC = 0;

data_in_PC = {16'b0000_0000_0000_0000,ins[16:1]};

end

เป็นคำสั่งที่ใช้ในการกระโดด ซึ่งรูปแบบในการกระโดด นั้นจะมีทั้ง เก็บค่า address ณ ตำแหน่งที่เริ่มกระโดด และแบบกระโดดแบบมีเงื่อนไข ซึ่งการกระโดดแบบมีเงื่อนไขจะทำการตรวจสอบ flag ก่อนการนำค่าไปให้โมดูล PC

Carry-look-ahead Adders 16 Bit 2’ Complement

ลักษณะการบวกเลขฐานสองสี่บิตและบิตเศษ ซึ่งจะบวกกันบิตต่อบิต และส่ง Carry ต่อ ไปให้บิตสูงกว่า

Figure 3: Addition of two 4-bit numbers illustrating the generation of the carry-out bit

การออกแบบวงจร เริ่มจากการออกแบบวงจรบวก 1 bit ซึ่งมีสมการการบวก และหาค่า Carry ของแต่ล่ะบิตดั้งนี้

Si = Ai Bi Ci = Pi Ci.                                                          (1)

COUT = Ci+1 = Ai.Bi + (Ai Bi).Ci.                        (2)

สำหรับในการออกแบบวงจรบวกจะใช้วิธีการคำนวณ Carry โดยวิธีแบบ Multi Level Carry Look ahead ซึ่งจะต้องทำการคำนวณค่า G,P เพื่อส่งต่อ Carry ไปใช้ในการคำนวล Carry ของ level ที่สูงขึ้น มีสมการหาค่า Carry และ G,P ดังนี้

Ci+1 = Gi + Pi.Ci                                                     (3)

Gi = Ai.Bi                           (4)

Pi = (Ai Bi)                        (5)

สมการการหาค่า Carry ในวงจร adder_4_bit โดยการคำนวณจากค่า G, P ใน level 1

C1 = G0 + P0.C0                                                                       (6) C2 = G1 + P1.C1 = G1 + P1.G0 + P1.P0.C0                               (7) C3 = G2 + P2.G1 + P2.P1.G0 + P2.P1.P0.C0                               (8) C4 = G3 + P3.G2 + P3.P2.G1 + P3P2.P1.G0 + P3P2.P1.P0.C0      (9)

Figure 4: Ripple-carry adder, illustrating the delay of the carry bit.

Figure 5: Block diagram of a 4-bit CLA.

สมการการหาค่า Carry ในวงจร adder_n_bit ในที่นี้คือ 16 bit โดยการคำนวณจากค่า G, P ใน level 2

PG = P3.P2.P1.P0                                                                (10)

GG = G3 + P3G2 + P3.P2.G1. + P3.P2.P1.G0                         (11)

Figure 6: Block diagram of a 16-bit CLA Adder

Booth algorithm

การทำงานของโมดูล Booth16x16 เป็นการใช้งาน Booth algorithm ในการคูณเลขขนาด 16 บิตเพื่อให้ลดจำนวนพื้นที่ที่ใช้ในการบวกค่าที่ได้จากการคูณแบบปกติ คือ ต้องบวกค่าถึง 16 ครั้ง แต่เมื่อมีการใช้งาน Booth algorithm จะลดการทำงานได้ถึงครึ่งหนึ่งซึ่งทำให้มีความรวดเร็วในการทำงานมากขึ้น

หลักการทำงานของ Booth algorithm

จะมีตัวตั้งสำหรับการงาน ให้เรานำตัวคูณเติม 0 ไว้ท้าย lsb จากนั้นจับกลุ่ม ครั้งละ 3 บิตจาก lsb โดยการจับกลุ่มครั้งต่อไปจับกลุ่มโดยซ้ำกับบิตสุดท้ายของกลุ่มก่อนหน้า ซึ่งจะได้จำนวนกลุ่มออกมา คือ n/2 เมื่อ n คือ จำนวนบิตที่ใช้ในการคูณ จากนั้นให้ทำงานตามตารางด้านล่างนี้

ค่าเลขฐานสอง

การทำงาน

000

0

001

+1

010

+1

011

+2

100

-2

101

-1

110

-1

111

0

0 คือ ค่าจากกลุ่มที่ทำงานนี้เท่ากับ 0

+1 คือ ค่าจากกลุ่มที่ทำงานนี้ให้นำตัวตั้งมา shift bit ตามหมายเลขกลุ่มลบหนึ่งแล้วคูณสอง เช่น กลุ่มที่ 1 ไม่มีการ shift bit, กลุ่มที่ 3 shift bit ตัวตั้ง (3-1)*2 = 4 บิต เป็นต้น

+2 คือ ค่าจากกลุ่มที่ทำงานนี้ให้ทำเหมืนการทำงาน +1 และให้ shift bit เพิ่มอีก 1

-1 คือ ค่าจากกลุ่มที่ทำงานนี้ทำงานเหมือนกับการทำงาน +1 แต่ก่อนการ shift bit ให้แปลงเลขตัวตั้งด้วยวิธี 2’s complement ก่อน

-2 คือ ค่าจากกลุ่มที่ทำงานนี้ทำงานเหมือนกับการทำงาน +2 แต่ก่อนการ shift bit ให้แปลงเลขตัวตั้งด้วยวิธี 2’s complement ก่อน

หลังจากได้ค่าจากกลุ่มแล้วให้นำค่าทั้งหมดมาบวกกัน ก็จะได้ค่าจากการคูณตัวเลข 2 ตัว

ตัวอย่างการทำงาน

1001 * 1011 หรือ -7 * -5

ขั้นตอนที่ 1 เติม 0 ที่บิตท้ายของตัวคูณได้ 10110

ขั้นตอนที่ 2 แบ่งกลุ่มออกตามการทำงานข้างต้นได้ 2 กลุ่ม คือ 110 และ 101

ขั้นตอนที่ 3 ดูค่าจากตาราง 110 เท่ากับ –1 และ 101 เท่ากับ -1

กลุ่ม 110 ได้ค่าตัวเลข 00000111

กลุ่ม 101 ได้ค่าตัวเลข 00011100

ขั้นตอนที่ 4 นำค่าที่ได้มาบวกกันได้ 00100011 การทำงานนี้ไม่สนใจตัวทดที่เกินจากจำนวนบิตที่ได้จากการคูณกันได้ 00100011 แปลงเป็นเลขฐาน 10 ได้ 35 ซึ่งเท่ากับ -7 * -5

Code Program

module CPU(clk,rst);

input clk,rst;

wire [31:0] addr,ins;

wire [31:0] s1,s2,data_in,data_out1,data_out2;

wire flag;

wire [31:0] m_r0,m_r1,m_r2,m_r3,m_r4,m_mem0,m_mem1;

reg clk2 = 0;

reg rw_mem = 0;

reg count2 = 0;

reg [1:0] count = 0;

reg rw_register = 0,rw_memory = 0,bw_memory = 1,rw_PC = 0;

reg sel = 0;

reg [31:0] data_in_PC = 0;

reg [4:0] addr_des = 0,addr_s1 = 0,addr_s2 = 0;

initial $monitor("op:%b r0:%d r1:%d r2:%d r3:%d r4:%d mem0:%d mem1:%d PC:%d clk2:%b step:%d flag:%d",ins[31:27],m_r0,m_r1,m_r2,m_r3,m_r4,m_mem0,m_mem1,addr,clk2,count,flag);

PC pc1(addr,clk2,rst,rw_PC,data_in_PC);

mem_ins mem1(ins,addr,rw_mem);

register regis1(m_r0,m_r1,m_r2,m_r3,m_r4,s1,s2,addr_des,addr_s1,addr_s2,rw_register,data_in);

ALU alu1(data_out1,flag,s1,s2,ins[16:1],ins[31:27],ins[0]);

data_mem mem2(m_mem0,m_mem1,data_out2,rw_memory,bw_memory,data_out1,s1);

always @(posedge clk)

begin

if(count2 == 0)

begin

clk2 = ~clk2;

count2 = 1;

end

else if(count2 == 1)

begin

count2 = 0;

end

end

always @(posedge clk)

begin

if(count == 0)

begin

count = 1;

rw_register = 0;

rw_memory = 0;

bw_memory = 1;

sel = 0;

end

else if(count == 1)

begin

count = 2;

rw_register = 0;

rw_memory = 0;

rw_PC = 0;

bw_memory = 1;

sel = 0;

end

else if(count == 2)

begin

count = 3;

rw_register = 0;

rw_memory = 0;

rw_PC = 0;

bw_memory = 1;

sel = 0;

case(ins[31:27])

5'b01000: begin// LDB

addr_s1 = 0;

addr_s2 = ins[21:17];

addr_des = ins[26:22];

end

5'b01001: begin// LDW

addr_s1 = 0;

addr_s2 = ins[21:17];

addr_des = ins[26:22];

end

5'b01010: begin// STB

addr_s1 = ins[26:22];

addr_s2 = ins[21:17];

addr_des = 0;

end

5'b01011: begin// STW

addr_s1 = ins[26:22];

addr_s2 = ins[21:17];

addr_des = 0;

end

5'b01101: begin// BEQ

addr_s1 = ins[26:22];

addr_s2 = ins[21:17];

addr_des = 0;

end

5'b01110: begin// BNEQ

addr_s1 = ins[26:22];

addr_s2 = ins[21:17];

addr_des = 0;

end

default: begin

addr_des = ins[26:22];

addr_s1 = ins[21:17];

addr_s2 = ins[16:12];

end

endcase

end

else if(count == 3)

begin

count = 0;

case(ins[31:27])

5'b00000: begin// ADD

rw_register = 1;

end

5'b00001: begin// ADDI

rw_register = 1;

end

5'b00010: begin// SUB

rw_register = 1;

end

5'b00011: begin// OR

rw_register = 1;

end

5'b00100: begin// XOR

rw_register = 1;

end

5'b00101: begin// SHL

rw_register = 1;

end

5'b00110: begin// SHR

rw_register = 1;

end

5'b00111: begin// AND

rw_register = 1;

end

5'b01000: begin// LDB

rw_register = 1;

rw_memory = 0;

bw_memory = 0;

sel = 1;

end

5'b01001: begin// LDW

rw_register = 1;

rw_memory = 0;

bw_memory = 1;

sel = 1;

end

5'b01010: begin// STB

rw_register = 0;

rw_memory = 1;

bw_memory = 0;

sel = 1;

end

5'b01011: begin// STW

rw_register = 0;

rw_memory = 1;

bw_memory = 1;

sel = 1;

end

5'b01100: begin// JMP

rw_PC = 1;

data_in_PC = {8'b0000_0000,ins[26:3]};

end

5'b01101: begin// BEQ

if(flag == 1)

rw_PC = 1;

else if(flag == 0)

rw_PC = 0;

data_in_PC = {16'b0000_0000_0000_0000,ins[16:1]};

end

5'b01110: begin// BNEQ

if(flag == 1)

rw_PC = 1;

else if(flag == 0)

rw_PC = 0;

data_in_PC = {16'b0000_0000_0000_0000,ins[16:1]};

end

5'b01111: begin// MUL

rw_register = 1;

end

5'b11111:;

default:begin

rw_register = 0;

rw_memory = 0;

end

endcase

end

end

assign data_in = sel ? data_out2 : data_out1;

endmodule

module PC(addr,clk,rst,rw,data_in);

output [31:0] addr; // sent to mem_ins

input clk,rst,rw;

input [31:0] data_in;

reg [31:0] reg_addr;

always @(posedge clk)

begin

if(rw == 0)

begin

if(rst == 0)

reg_addr = 0;

else if(rst == 1)

reg_addr = reg_addr+1;

end

else if(rw == 1)

begin

reg_addr = data_in;

end

end

assign addr = reg_addr;

endmodule

module mem_ins(ins,addr,rw);

output [31:0] ins;

input [31:0] addr; //recive from PC

input rw;

reg [31:0] mem[0:31];

reg [31:0] reg_ins;

initial

begin

mem[0] = 32'b11111_00000_00000_00000_00000_00000_00;// NOP

mem[1] = 32'b00000_00010_00001_00000_00000_00000_00;// ADD r2 r1 r0

mem[2] = 32'b00111_00000_00011_00100_00000_00000_00;// AND r0 r3 r4

mem[3] = 32'b00010_00010_00010_00010_00000_00000_00;// SUB r2 r2 r2

mem[4] = 32'b00100_00100_00010_00001_00000_00000_00;// XOR r4 r2 r1

mem[5] = 32'b00101_00100_00001_00011_00000_00000_00;// SHL r4 r1 r3

mem[6] = 32'b01001_00010_00000_00000_00000_00000_10;// LDW r2 [#1]

mem[7] = 32'b00110_00010_00010_00001_00000_00000_00;// SHR r2 r2 r1

mem[8] = 32'b01110_00010_00001_00000_00000_00011_10;// BNEQ r2 r1 #7

mem[9] = 32'b01011_00010_00000_00000_00000_00000_01;// STW r2 [r0]

mem[10] = 32'b00001_00000_00011_00000_00000_00001_10;// ADDI r0 r3 #3

end

always @(addr)

begin

if(rw == 0)

begin

reg_ins = mem[addr];

end

end

assign ins = reg_ins;

endmodule

module register(m_r0,m_r1,m_r2,m_r3,m_r4,s1,s2,addr_des,addr_s1,addr_s2,rw,data_in);

output [31:0] s1,s2;

output [31:0] m_r0,m_r1,m_r2,m_r3,m_r4;

input [31:0] data_in;

input [4:0] addr_des,addr_s1,addr_s2;

input rw;

reg [31:0] reg_s1,reg_s2;

reg [31:0] m_r0,m_r1,m_r2,m_r3,m_r4;

reg [31:0]regis[0:31];

initial

begin

regis[0] = 0;

regis[1] = 1;

regis[2] = 2;

regis[3] = 3;

regis[4] = 4;

regis[5] = 5;

regis[6] = 6;

regis[7] = 7;

regis[8] = 8;

end

always @(*)

begin

if(rw == 0)

begin

reg_s1 = regis[addr_s1];

reg_s2 = regis[addr_s2];

end

else if(rw == 1)

begin

regis[addr_des] = data_in;

end

m_r0 = regis[0];

m_r1 = regis[1];

m_r2 = regis[2];

m_r3 = regis[3];

m_r4 = regis[4];

end

assign s1 = reg_s1;

assign s2 = reg_s2;

endmodule

module ALU(data_out,flag,s1,s2,immed,sel,sel2);

output [31:0] data_out;

output flag;

input [31:0] s1,s2;

input [4:0] sel;

input [15:0] immed;

input sel2;

reg [31:0] reg_data_out;

wire [31:0] reg_adder,reg_booth;

reg reg_flag;

reg [31:0] count;

reg [31:0] reg_s1,reg_s2;

reg cout;

adder_n_bit a1(s1,s2,1'b0,reg_adder,);

Booth16x16 B1(s1[15:0],s2[15:0],reg_booth);

always @(*)

begin

reg_flag = 0;

case(sel)

5'b00000: reg_data_out = reg_adder;

5'b00001: begin

if(immed[15] == 0)

reg_data_out = s1+{16'b0000_0000_0000_0000,immed};

else if(immed[15] == 1)

reg_data_out = s1+{16'b1111_1111_1111_1111,immed};

end

5'b00010: reg_data_out = s1-s2;

5'b00011: reg_data_out = s1|s2;

5'b00100: reg_data_out = s1^s2;

5'b00101: begin

reg_data_out = s1;

for(count = 0;count < s2;count = count+1)

begin

reg_data_out = {reg_data_out[30:0],1'b0};

end

end

5'b00110: begin

reg_data_out = s1;

for(count = 0;count < s2;count = count+1)

begin

reg_data_out = {1'b0,reg_data_out[31:1]};

end

end

5'b00111: reg_data_out = s1&s2;

5'b01000: begin

if(sel2 ==0)

reg_data_out = immed;

else if(sel2 == 1)

reg_data_out = s2;

end

5'b01001: begin

if(sel2 == 0)

reg_data_out = immed;

else if(sel2 == 1)

reg_data_out = s2;

end

5'b01010: begin

if(sel2 == 0)

reg_data_out = immed;

else if(sel2 == 1)

reg_data_out = s2;

end

5'b01011: begin

if(sel2 == 0)

reg_data_out = immed;

else if(sel2 == 1)

reg_data_out = s2;

end

5'b01100:;

5'b01101: begin

if(s1 == s2)

reg_flag = 1;

end

5'b01110: begin

if(s1 != s2)

reg_flag = 1;

end

5'b01111:reg_data_out = reg_booth;

5'b11111:;

endcase

end

assign data_out = reg_data_out;

assign flag = reg_flag;

endmodule

module data_mem(m_mem0,m_mem1,data_out,rw,bw,addr,data_in);

output [31:0] data_out;

output [31:0] m_mem0,m_mem1;

input rw,bw;

input [31:0] addr,data_in;

reg [31:0] reg_data_out;

reg [31:0] mem[0:15];

reg [31:0] reg_m_mem0,reg_m_mem1;

initial

begin

mem[0] = 000;

mem[1] = 111;

mem[2] = 222;

mem[3] = 333;

mem[4] = 444;

mem[5] = 555;

mem[6] = 666;

mem[7] = 777;

mem[8] = 888;

mem[9] = 9'b111111111;

end

always@ (*)

begin

if(rw == 0)

begin

reg_data_out = mem[addr];

end

else if(rw == 1)

begin

mem[addr] = data_in;

end

reg_m_mem0 = mem[0];

reg_m_mem1 = mem[1];

end

assign data_out = bw ? reg_data_out:{24'b0000_0000_0000_0000_0000_0000,reg_data_out[7:0]};

assign m_mem0=reg_m_mem0;

assign m_mem1=reg_m_mem1;

endmodule

module full_adder(a,b,cin,sum,cout);

input a, b, cin;

output sum, cout;

assign sum = cin ^ a ^ b; // cin XOR a XOR b

assign cout = ~cin & a & b | cin & (a | b); // cin'ab + cin(a + b)

endmodule // note no semicolon

module adder_4_bit(x,y,cin,z,cout); //z=x+y

input [3:0] x, y; // x and y are 4-bit inputs

input cin;

output [3:0] z;

output cout;

wire [3:1] carry; // used for internal carries between FA's

// 4 1-bit full adder instances

// - note how carry propagates between FA's

// - the labels fa0,fa1,fa2,fa3 are optional

full_adder fa0(x[0],y[0],cin,z[0],carry[1]);

full_adder fa1(x[1],y[1],carry[1],z[1],carry[2]);

full_adder fa2(x[2],y[2],carry[2],z[2],carry[3]);

full_adder fa3(x[3],y[3],carry[3],z[3],cout);

endmodule

// the following is a generic adder that can be instantiated for 4 bits, or

// 8 bits or whatever you need

module adder_n_bit(x,y,cin,z,cout); //z=x+y

parameter n=32; // default is 4 but can be overridden when instantiated

input [n-1:0] x, y;

input cin;

output [n-1:0] z;

output cout;

wire [n-1:0] carry_in; // used for internal carries between FA's

wire [n-1:0] carry_out; // used for internal carries between FA's

assign carry_in[0] = cin;

assign cout = carry_out[n-1];

assign carry_in[n-1:1] = carry_out[n-2:0]; // connect cout's to cin's

// n-bit arry of 1-bit full adders

full_adder fa[n-1:0] (x,y,carry_in,z,carry_out);

// generate loops were added to the Verilog-2001 standard which are

// more powerful than arrays of instances but aren't supported yet in

// iverilog

endmodule

module Booth16x16(A,B,out);

input [15:0] A,B;

output [31:0] out;

reg [2:0] booth_reg[7:0];

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

reg [3:0] i;

reg [31:0] out_reg = 0;

always@(A,B)

begin

booth_reg[0] = B[1:0];

booth_reg[0] = booth_reg[0] << 1;

booth_reg[1] = B[3:1];

booth_reg[2] = B[5:3];

booth_reg[3] = B[7:5];

booth_reg[4] = B[9:7];

booth_reg[5] = B[11:9];

booth_reg[6] = B[13:11];

booth_reg[7] = B[15:13];

for(i = 0;i < 8;i = i+1) begin

if(booth_reg[i] == 3'b000 || booth_reg[i] == 3'b111) begin

value[i] = 0;

end

else if(booth_reg[i] == 3'b001 || booth_reg[i] == 3'b010) begin

value[i] = A;

value[i] = value[i] << (i*2);

end

else if(booth_reg[i] == 3'b011) begin

value[i] = A;

value[i] = value[i] << (i*2);

value[i] = value[i] << 1;

end

else if(booth_reg[i] == 3'b100) begin

value[i] = A;

value[i] = ~value[i];

value[i] = value[i] + 1;

value[i] = value[i] << (i*2);

value[i] = value[i] << 1;

end

else if(booth_reg[i] == 3'b101 || booth_reg[i] == 3'b110) begin

value[i] = A;

value[i] = ~value[i];

value[i] = value[i] + 1;

value[i] = value[i] << (i*2);

end

end

out_reg = value[0] + value[1] + value[2] + value[3] + value[4] + value[5] + value[6] + value[7];

end

assign out = out_reg;

endmodule Instruction formats

Opcode

Instruction

Action

00000

ADD r1, r2, r3

r1 = r2 + r3

00001

ADDI r1, r2, #immed

r1 = r2 + immed(16 bit)

00010

SUB r1, r2, r3

r1 = r2 - r3

00011

OR r1, r2, r3

r1 = r2 OR r3

00100

XOR r1, r2, r3

r1 = r2 XOR r3

00101

SHL r1, r2, r3

r1 = r2 shift left r3 time

00110

SHR r1, r2, r3

r1 = r2 shift right r3 time

00111

AND r1, r2, r3

r1 = r2 AND r3

01000

LDB r1, [r2 or immed]

Sel = 0, r1 = 8 bit of mem[immed(16 bit)]

Sel = 1, r1 = 8 bit of mem[r2]

01001

LDW r1, [r2 or immed]

Sel = 0, r1 = mem[immed(16 bit)]

Sel = 1, r1 = mem[r2]

01010

STB r1, [r2 or immed]

Sel = 0, 8 bit of mem[immed(16 bit)] = r1

Sel = 1, 8 bit of mem[r2] =r1

01011

STW r1, [r2 or immed]

Sel = 0, mem[immed(16 bit)] = r1

Sel = 1, mem[r2] = r1

01100

JMP [immed]

PC = immed(24 bit);

01101

BEG r1, r2, [immed]

If(r1 = r2) PC = immed(16 bit);

01110

BNEG r1, r2, [immed]

If(r1 ≠ r2) PC = immed(16 bit);

01111

MUL r1, r2, r3

r1 = r2 * r3

11111

NOP

No Operation

1. For instruction

ADD, SUB, OR, XOR, SHL, SHR, AND, MUL

(26) (31) (0) (11) (16) (21)

OP

r1

r2

r3

2. For instruction

ADDI, BEQ, BNEQ

(21) (26) (31) (0) (1) (16)

OP

r1

r2

immed

3. For instruction

LDB, LDW, STB, STW

(26) (31) (1) (16) (21) (0)

OP

r1

r2

immed

sel

4. For instruction

JMP

(26) (31) (0) (3)

OP

immed

5. For instruction

NOP

(0) (26) (31)

OP