fivedots.coe.psu.ac.thfivedots.coe.psu.ac.th/Software.coe/241-440/CSD/csd_1/… · Web...
Transcript of fivedots.coe.psu.ac.thfivedots.coe.psu.ac.th/Software.coe/241-440/CSD/csd_1/… · Web...
การออกแบบ 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