Tiny GPU: GPU ขั้นต่ำที่สร้างด้วย Verilog
(github.com/adam-maj)- tiny-gpu คือการอิมพลีเมนต์ GPU ขั้นต่ำบนพื้นฐานของ Verilog สำหรับเรียนรู้จากระดับล่างสุดว่า GPU ทำงานอย่างไรในระดับฮาร์ดแวร์ โดยเน้นหลักการร่วมกันของ GPGPU และตัวเร่ง ML มากกว่าฮาร์ดแวร์เฉพาะงานกราฟิก
- การอิมพลีเมนต์ประกอบด้วย ไฟล์ Verilog ที่มีเอกสารกำกับน้อยกว่า 15 ไฟล์, เอกสารสถาปัตยกรรมและ ISA, เคอร์เนลบวกและคูณเมทริกซ์, รวมถึงการรองรับการจำลองเคอร์เนลและการติดตามการทำงาน
- GPU รันได้ทีละ เคอร์เนลเดียว โดยโหลดหน่วยความจำโปรแกรมและหน่วยความจำข้อมูล ตั้งค่า
thread_countแล้วส่งสัญญาณstartเพื่อเริ่มเคอร์เนล - เพื่อให้เรียบง่าย แต่ละคอร์จะประมวลผล block ได้ครั้งละหนึ่งชุด และแต่ละเธรดมี ALU, LSU, PC และ register file ของตัวเอง แต่สมมติว่าเธรดทั้งหมดจะกลับมารวมที่ PC เดียวกันหลังทุกคำสั่ง
- ฟีเจอร์ส่วนใหญ่ของ GPU สมัยใหม่ เช่น แคชหลายชั้น, shared memory, memory coalescing, pipelining, warp scheduling, branch divergence และ barrier ถูกตัดออกเพื่อให้ความสำคัญกับโครงสร้างสำหรับการเรียนรู้
ปัญหาที่ tiny-gpu พยายามแก้
- ฝั่ง CPU มีสื่อให้เรียนรู้ตั้งแต่สถาปัตยกรรมไปจนถึงสัญญาณควบคุมอยู่มาก แต่รายละเอียดทางเทคนิคระดับต่ำของ GPU สมัยใหม่ส่วนใหญ่ยังเป็นกรรมสิทธิ์เพราะตลาดมีการแข่งขันสูง
- แม้จะมีสื่อการเขียนโปรแกรม GPU มากมาย แต่แทบไม่มีสื่อที่ช่วยให้เรียนรู้ว่า GPU ทำงานอย่างไรใน ระดับฮาร์ดแวร์
- อิมพลีเมนต์ GPU แบบโอเพนซอร์สอย่าง Miaow และ VeriGPU มุ่งให้ทำงานได้ครบและใช้งานจริง จึงมีโครงสร้างซับซ้อน
- tiny-gpu ตัดความซับซ้อนจำนวนมากของการ์ดจอระดับโปรดักชันออก แล้วโฟกัสกับองค์ประกอบแกนหลักที่ตัวเร่งฮาร์ดแวร์สมัยใหม่มีร่วมกัน
- องค์ประกอบสำคัญของสถาปัตยกรรม GPU
- วิธีที่โมเดลการเขียนโปรแกรมแบบ SIMD ถูกอิมพลีเมนต์ในฮาร์ดแวร์
- วิธีที่ GPU จัดการกับแบนด์วิดท์หน่วยความจำที่จำกัด
สถาปัตยกรรมโดยรวม
- tiny-gpu ถูกออกแบบให้รันได้ ทีละเคอร์เนลเดียว
- ขั้นตอนการรันเคอร์เนลมีดังนี้
- โหลดโค้ดเคอร์เนลเข้า global program memory
- โหลดข้อมูลที่ต้องใช้เข้า data memory
- ระบุจำนวนเธรดที่จะรันใน device control register
- ตั้งสัญญาณ
startเป็น high เพื่อเริ่มรันเคอร์เนล
- GPU ประกอบด้วยยูนิตต่อไปนี้
- device control register
- dispatcher
- compute core จำนวนแปรผัน
- memory controller สำหรับ data memory และ program memory
- cache
การรันเคอร์เนลและการกระจายเธรด
- device control register ทำหน้าที่เก็บเมทาดาทาของการรันเคอร์เนล และใน tiny-gpu จะเก็บเพียง
thread_countซึ่งเป็นจำนวนเธรดทั้งหมดที่จะรัน - dispatcher จะกระจายเธรดไปยัง compute core หลายตัวเมื่อเริ่มเคอร์เนล
- จัดกลุ่มเธรดที่สามารถรันแบบขนานได้เป็น block
- ส่ง block ไปให้คอร์ที่พร้อมใช้งานประมวลผล
- แจ้งว่าการรันเคอร์เนลเสร็จสิ้นเมื่อประมวลผลทุก block ครบแล้ว
- คอร์แบบย่อส่วนนี้ประมวลผลได้ครั้งละหนึ่ง block
- แต่ละเธรดมี ALU, LSU, PC และ register file เฉพาะตัว
- การจัดการการรันคำสั่งของเธรดบนทรัพยากรเหล่านี้เป็นหนึ่งในปัญหาที่ยากของ GPU
โครงสร้างหน่วยความจำและคอนโทรลเลอร์
- GPU ถูกสร้างให้เชื่อมต่อกับ global memory ภายนอก และเพื่อความเรียบง่ายจึงแยก data memory ออกจาก program memory
- สเปกของ data memory
- addressable แบบ 8 บิต
- มีทั้งหมด 256 แถว
- ข้อมูลขนาด 8 บิต
- แต่ละแถวเก็บค่าได้ต่ำกว่า 256
- สเปกของ program memory
- addressable แบบ 8 บิต
- มีทั้งหมด 256 แถว
- ข้อมูลขนาด 16 บิต
- แต่ละคำสั่งมีขนาด 16 บิตตาม ISA
- memory controller จะติดตามคำขอหน่วยความจำจากคอร์ จำกัดคำขอให้สอดคล้องกับแบนด์วิดท์ของหน่วยความจำภายนอกจริง และส่งต่อคำตอบกลับไปยังทรัพยากรที่ถูกต้อง
- memory controller แต่ละตัวมีจำนวนช่องสัญญาณคงที่ตามแบนด์วิดท์ของ global memory
- cache ยังอยู่ระหว่างพัฒนา โดยจะเก็บข้อมูลที่ดึงมาจากหน่วยความจำภายนอกไว้ใน SRAM ภายในอุปกรณ์ เพื่อให้คำขอครั้งถัดไปดึงได้เร็วขึ้น และเปิดแบนด์วิดท์หน่วยความจำไว้สำหรับข้อมูลใหม่
องค์ประกอบภายในคอร์
- แต่ละคอร์มี scheduler ตัวเดียวสำหรับจัดการการรันของเธรด
- scheduler ของ tiny-gpu จะรันคำสั่งของหนึ่ง block ไปจนจบก่อนจึงค่อยรับ block ใหม่ และรันคำสั่งของทุกเธรดตามลำดับที่ซิงก์กัน
- scheduler ที่ก้าวหน้ากว่านี้สามารถเพิ่มอัตราการใช้ทรัพยากรด้วย pipelining และ warp scheduling
- ข้อจำกัดหลักของ scheduler คือ latency ที่เกิดขึ้นเมื่อโหลดและบันทึกข้อมูลจาก global memory
- คำสั่งส่วนใหญ่รันแบบ synchronous ได้
- งาน load-store อย่าง
LDRและSTRเป็นแบบ asynchronous จึงต้องจัดโครงสร้างการรันคำสั่งรอบช่วงรอที่ยาว
- Fetcher จะดึงคำสั่งของ program counter ปัจจุบันจาก program memory แบบ asynchronous
- Decoder จะแปลงคำสั่งที่ดึงมาเป็นสัญญาณควบคุมสำหรับการรันของเธรด
- register file ของแต่ละเธรดใช้เก็บข้อมูลระหว่างคำนวณและทำให้เกิดแพตเทิร์น SIMD
- register แบบอ่านอย่างเดียวมี
%blockIdx,%blockDim,%threadIdx - เคอร์เนลสามารถรันกับข้อมูลที่ต่างกันตาม local thread ID
- register แบบอ่านอย่างเดียวมี
- ALU ของแต่ละเธรดรองรับคำสั่งเลขคณิต
ADD,SUB,MUL,DIV CMPจะให้ผลว่าค่าผลต่างของสองรีจิสเตอร์เป็นลบ ศูนย์ หรือบวก และเก็บผลไว้ในรีจิสเตอร์NZPของยูนิต PC- LSU ของแต่ละเธรดเข้าถึง global data memory และจัดการ
LDR,STRรวมถึงเวลารอหน่วยความจำแบบ asynchronous - PC ของแต่ละเธรดกำหนดคำสั่งถัดไปที่จะรัน
- โดยปกติจะเพิ่มขึ้นทีละ 1 ต่อหนึ่งคำสั่ง
BRnzpจะกระโดดไปยังแถวของ program memory ที่กำหนด หากเงื่อนไขในรีจิสเตอร์NZPที่ตั้งโดยCMPก่อนหน้าตรงกัน- ลูปและเงื่อนไขถูกอิมพลีเมนต์ด้วยวิธีนี้
- เพื่อความเรียบง่าย tiny-gpu สมมติว่าเธรดทั้งหมดจะกลับมารวมที่ PC เดียวกันหลังทุกคำสั่ง
- ใน GPU จริง แต่ละเธรดสามารถแตกแขนงไปยัง PC ที่ต่างกันได้ และเมื่อนั้นจะเกิด branch divergence ซึ่งทำให้กลุ่มเธรดที่เคยถูกประมวลผลร่วมกันแยกออกเป็นหลาย execution flow
ISA
- tiny-gpu อิมพลีเมนต์ ISA 11 คำสั่ง เพื่อรันเคอร์เนลเรียบง่ายสำหรับพิสูจน์แนวคิด เช่น การบวกเมทริกซ์และการคูณเมทริกซ์
- คำสั่งที่รองรับ
BRnzp: กระโดดไปยังแถวอื่นของ program memory หากเงื่อนไขNZPตรงกันCMP: เปรียบเทียบค่าในสองรีจิสเตอร์และบันทึกผลลงรีจิสเตอร์NZPADD,SUB,MUL,DIV: การคำนวณเลขคณิตพื้นฐานสำหรับ tensor mathLDR: โหลดข้อมูลจาก global memorySTR: บันทึกข้อมูลลง global memoryCONST: โหลดค่าคงที่เข้ารีจิสเตอร์RET: ส่งสัญญาณจบการรันของเธรดปัจจุบัน
- แต่ละรีจิสเตอร์ถูกระบุด้วย 4 บิต จึงมีทั้งหมด 16 รีจิสเตอร์
R0ถึงR12จำนวน 13 ตัวเป็นรีจิสเตอร์อิสระที่อ่านและเขียนได้- 3 ตัวสุดท้ายเป็น special register แบบอ่านอย่างเดียวที่ให้ค่า
%blockIdx,%blockDim,%threadIdxซึ่งจำเป็นต่อ SIMD
ลำดับการรัน
- เมื่อแต่ละคอร์รันคำสั่ง จะใช้ลำดับการควบคุมในขั้นต่อไปนี้
FETCH: ดึงคำสั่งถัดไปของ PC ปัจจุบันDECODE: ถอดรหัสคำสั่งเป็นสัญญาณควบคุมREQUEST: ขอข้อมูลจาก global memory หากเป็นLDRหรือSTRWAIT: รอการตอบกลับจาก global memory หากจำเป็นEXECUTE: ทำการคำนวณกับข้อมูลUPDATE: อัปเดต register file และรีจิสเตอร์NZP
- ลำดับการควบคุมนี้ถูกออกแบบมาเพื่อความเรียบง่ายและเข้าใจได้ง่าย
- ในการอิมพลีเมนต์จริง อาจยุบรวมบางขั้นตอนเพื่อปรับเวลาในการประมวลผลให้เหมาะสม หรือใช้ pipelining เพื่อจัดการการรันหลายคำสั่งบนทรัพยากรของคอร์
- แต่ละเธรดจะคำนวณตาม execution path เดียวกันโดยอาศัยข้อมูลใน register file เฉพาะของตน
- แผนภาพคล้ายกับของ CPU แต่ต่างกันตรงที่มี
%blockIdx,%blockDim,%threadIdxอยู่ในรีจิสเตอร์แบบอ่านอย่างเดียวเพื่อเปิดใช้ความสามารถแบบ SIMD
ตัวอย่างเคอร์เนล
- มีการเขียนเคอร์เนลบวกเมทริกซ์และคูณเมทริกซ์เพื่อพิสูจน์แนวคิดของ ISA
- ไฟล์ทดสอบในรีโพซิทอรีสามารถจำลองเคอร์เนลเหล่านี้บน GPU ได้ครบถ้วน และสร้างสถานะของ data memory พร้อม execution trace ทั้งหมด
-
การบวกเมทริกซ์
matadd.asmใช้บวกเมทริกซ์ขนาด 1 x 8 สองชุด- การบวกทีละองค์ประกอบทั้ง 8 จะทำในเธรดแยกกัน
- ใช้รีจิสเตอร์
%blockIdx,%blockDim,%threadIdxเพื่อแสดงการเขียนโปรแกรมแบบ SIMD - ใช้คำสั่ง
LDRและSTRจึงครอบคลุมการจัดการหน่วยความจำแบบ asynchronous
-
การคูณเมทริกซ์
matmul.asmใช้คูณเมทริกซ์ขนาด 2 x 2 สองชุด- คำนวณ dot product ของแถวและคอลัมน์ที่เกี่ยวข้องทีละองค์ประกอบ
- ใช้
CMPและBRnzpเพื่อแสดงการแตกแขนงภายในเธรด - เนื่องจากทุกแขนงกลับมารวมกันอีกครั้ง จึงทำงานได้ในอิมพลีเมนต์ tiny-gpu ปัจจุบัน
การจำลอง
- หากต้องการรันการจำลองเคอร์เนล ต้องมี iverilog และ cocotb
- ขั้นตอนเตรียมพร้อม
- ติดตั้ง Verilog compiler และ cocotb ด้วย
brew install icarus-verilogและpip3 install cocotb - ดาวน์โหลดเวอร์ชันล่าสุดของ sv2v แตกไฟล์ และเพิ่มไบนารีลงใน
$PATH - รัน
mkdir buildที่รูทของรีโพซิทอรี
- ติดตั้ง Verilog compiler และ cocotb ด้วย
- การจำลองเคอร์เนลรันได้ด้วย
make test_mataddและmake test_matmul - ผลการรันจะถูกเขียนเป็นไฟล์ล็อกใน
test/logs- สถานะเริ่มต้นของ data memory
- execution trace ทั้งหมดของเคอร์เนล
- สถานะสุดท้ายของ data memory
- ตอนต้นของแต่ละไฟล์ล็อกจะแสดงเมทริกซ์อินพุต และส่วนท้ายใน data memory สุดท้ายจะแสดงเมทริกซ์ผลลัพธ์
- execution trace มีสถานะการรันของทุกเธรดในทุกคอร์สำหรับแต่ละ cycle
- คำสั่งปัจจุบัน
- PC
- ค่ารีจิสเตอร์
- ข้อมูลสถานะ
ความสามารถขั้นสูงของ GPU ที่ตั้งใจไม่ใส่มา
- tiny-gpu ตัดองค์ประกอบเพิ่มประสิทธิภาพและความสามารถส่วนใหญ่ของ GPU สมัยใหม่ออกเพื่อให้เรียบง่าย
-
แคชหลายชั้นและ shared memory
- GPU สมัยใหม่ใช้ลำดับชั้นของแคชหลายระดับเพื่อลดการเข้าถึง global memory
- tiny-gpu อิมพลีเมนต์เพียงชั้นแคชเดียวที่เก็บข้อมูลล่าสุดไว้ระหว่างทรัพยากรที่ร้องขอกับ memory controller
- แคชหลายชั้นช่วยเก็บข้อมูลที่ใช้บ่อยให้ใกล้ตำแหน่งใช้งานมากขึ้นเพื่อลดเวลาโหลด
- GPU ยังอาจใช้ shared memory เพื่อให้เธรดใน block เดียวกันแลกเปลี่ยนผลลัพธ์ที่ใช้ร่วมกันได้
-
memory coalescing
- ระหว่างการรันแบบขนาน หลายเธรดมักเข้าถึงแอดเดรสต่อเนื่องกัน เช่น องค์ประกอบที่อยู่ติดกันของเมทริกซ์
- memory coalescing จะวิเคราะห์คำขอหน่วยความจำในคิวและรวมคำขอที่อยู่ติดกันให้เป็นทรานแซกชันเดียว
- เป้าหมายคือเพื่อลดเวลาในการอ้างแอดเดรสและประมวลผลคำขอร่วมกัน
-
pipelining
- คอร์ของ tiny-gpu จะเริ่มคำสั่งถัดไปได้ก็ต่อเมื่อการรันคำสั่งหนึ่งของชุดเธรดก่อนหน้าสิ้นสุดแล้ว
- GPU สมัยใหม่สามารถสตรีมการรันคำสั่งตามลำดับหลายคำสั่งได้ พร้อมยังคงรับประกันลำดับสำหรับคำสั่งที่มีการพึ่งพากัน
- ช่วยเพิ่มอัตราการใช้ทรัพยากรไม่ให้คอร์ว่างในช่วงอย่างเช่นการรอคำขอหน่วยความจำแบบ asynchronous
-
warp scheduling
- แบ่ง block ออกเป็น warp ซึ่งเป็นชุดเธรดที่สามารถรันร่วมกันได้
- เมื่อ warp หนึ่งกำลังรอ ก็จะรันคำสั่งของ warp อื่น ทำให้คอร์เดียวประมวลผลหลาย warp พร้อมกันได้
- คล้ายกับ pipelining แต่เป็นการจัดการคำสั่งจากคนละเธรด
-
branch divergence
- tiny-gpu สมมติว่าเธรดทั้งหมดในชุดเดียวกันจะมี PC เดียวกันหลังแต่ละคำสั่ง
- ในความเป็นจริง แต่ละเธรดสามารถแตกแขนงไปยังบรรทัดที่ต่างกันตามข้อมูลได้
- เธรดที่มี PC ต่างกันจะถูกแยกเป็น execution flow คนละชุด และยังต้องจัดการจุดที่กลับมารวมกันอีกครั้ง
-
การซิงก์และ barrier
- GPU สมัยใหม่สามารถตั้ง barrier เพื่อให้กลุ่มเธรดใน block เดียวกันรอจนทุกเธรดมาถึงจุดที่กำหนด
- มีประโยชน์เมื่อเธรดต้องแลกเปลี่ยนข้อมูลร่วมกัน และต้องการรับประกันว่าการประมวลผลข้อมูลเสร็จสมบูรณ์แล้ว
งานถัดไป
- รายการปรับปรุงในอนาคตมีดังนี้
- เพิ่ม instruction cache แบบง่าย
- สร้าง adapter เพื่อให้ใช้ GPU บน Tiny Tapeout 7 ได้
- เพิ่ม branch divergence ขั้นพื้นฐาน
- เพิ่ม memory coalescing ขั้นพื้นฐาน
- เพิ่ม pipelining ขั้นพื้นฐาน
- ปรับปรุง control flow และการใช้รีจิสเตอร์เพื่อให้ cycle time ดีขึ้น
- เขียนเคอร์เนลกราฟิกพื้นฐานหรือเพิ่มฮาร์ดแวร์กราฟิกแบบง่ายเพื่อสาธิตความสามารถด้านกราฟิก
- ผู้ใช้ที่ต้องการช่วยพัฒนารีโพซิทอรีสามารถมีส่วนร่วมผ่าน PR ได้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ข้อยกเว้นคือ Intel ที่เผยแพร่เอกสารเทคนิคเกี่ยวกับ GPU ไว้มากมาย: https://kiwitree.net/~lina/intel-gfx-docs/prm/
คู่มือ i810/815 ก็หาได้ออนไลน์ และถ้าไม่นับช่องว่างแปลก ๆ ในช่วงก่อน 965 ที่ขาด 855/910/915/945 ไป การจัดทำเอกสารก็ถือว่าค่อนข้างต่อเนื่อง
รวมถึง เอกสารสถาปัตยกรรมชุดคำสั่ง ของผลิตภัณฑ์ปัจจุบันและในอดีตด้วย แต่ดูเหมือนจะเป็นเอกสารสำหรับผู้พัฒนาการใช้งานจริงมากกว่าคำอธิบายระดับสูงสำหรับนักเล่นที่สนใจ
อยากให้ทุกบริษัททำตามแนวทางนี้
แต่ผมมองว่านี่ใกล้เคียงกับ โปรเซสเซอร์ร่วม SIMD มากกว่า
ถ้าจะเรียกว่า GPU อย่างน้อยก็ควรต้องมีเอาต์พุตแสดงผลบางรูปแบบ
ผมรู้ว่าช่วงหลังคำนี้ถูกใช้หลวมขึ้นมาก เพราะ Nvidia และบริษัทอื่น ๆ ขายสถาปัตยกรรมกราฟิกเวอร์ชันสำหรับเซิร์ฟเวอร์โดยเฉพาะในชื่อ GPU ด้วย แต่ในงานออกแบบ GPU ส่วนของกราฟิกก็ยังคงเป็นสัดส่วนความซับซ้อนที่มากอยู่ดี
GPU ที่ไม่ส่งภาพออกมาก็ยังมีประโยชน์
ที่ทำงานของผมมีเวิร์กสเตชันประมาณ 75 เครื่องที่ใส่ Quadro ระดับกลางไว้ การ์ดมีแต่ mini-DisplayPort แต่บริษัทซื้อให้แค่สาย HDMI ทุกเครื่องเลยต่อเข้ากราฟิกออนบอร์ดกันหมด
ถึงอย่างนั้นการ์ดพวกนั้นก็ยังเร่งซอฟต์แวร์และประมวลผลกราฟิก เพียงแค่ไม่ได้ส่งภาพขึ้นจอเท่านั้น
มีตัวอย่างอื่นด้วย: https://github.com/jbush001/NyuziProcessor
ถ้าจะให้ TSMC หรือฟาวน์ดรีอื่นผลิตโปรเซสเซอร์แบบนี้ได้คุ้มทุน ต้องมีปริมาณประมาณเท่าไร?
ผมอยากลอง FPGA แต่พูดตรง ๆ แค่จะเริ่มตรงไหนยังจับทางยาก และทั้งสาขาก็ดูน่าเกรงขามมาก
เป้าหมายสุดท้ายคือทำการ์ดเร่งความเร็วสำหรับ LLM ซึ่งแม้จะเป็นเป้าหมายที่ตั้งขึ้นค่อนข้างตามใจ แต่ก็น่าจะมีส่วนทับซ้อนกับโปรเจกต์นี้เยอะ และอาจต่างกันแค่ส่วน offloading หน่วยความจำเพื่อโหลดโมเดลที่ใหญ่ขึ้นเท่านั้น
การเริ่มต้นกับ FPGA ต้องแบ่งออกเป็นเทคโนโลยีย่อยหลายส่วน และต้องปรับความคาดหวังด้วย
เราไม่คาดหวังให้วิศวกรซอฟต์แวร์สร้างคอมพิวเตอร์ทั้งเครื่องจากหลักการพื้นฐาน เขียนสถาปัตยกรรมชุดคำสั่ง เข้าใจภาษาเครื่อง แปลงมันเป็นแอสเซมบลี และพัฒนาภาษาโปรแกรมเพื่อสร้างแอปด้วยโค้ด Python ตั้งแต่แรก
ควรเริ่มจากด้านบนแล้วค่อยไล่ลงไปตามสแต็ก
ถ้าโฟกัสที่การซ่อนความซับซ้อนและสร้างระบบจาก IP ที่ทำไว้แล้ว การออกแบบ FPGA ก็จะค่อนข้างง่าย
โดยทั่วไปจะแนะนำเครื่องมืออย่าง MATLAB เพราะถ้าใช้ DevKit ที่มี reference design ก็สามารถสร้างแอปเริ่มต้นด้วย HDL Coder ได้
ไม่อย่างนั้นจะต้องแบกรับภาระมหาศาลในการเรียนรู้สถาปัตยกรรมคอมพิวติ้งดิจิทัล, Verilog, timing, transceiver/I/O, การวางแผนขา, Quartus/Vivado, simulation/verification, ระบบ embedded ฯลฯ
สรุปคือให้เริ่มจาก การออกแบบระดับระบบ เรียนรู้วิธีนำ IP แบบ plug-and-play มาเชื่อมต่อกันที่ระดับบนสุด แล้วลองใส่โมดูลนั้นเข้าไปใน reference design ที่ทำไว้แล้ว
จากนั้นค่อย ๆ ลอกชั้นออกเพื่อเผยให้เห็นความซับซ้อนด้านล่าง
สามารถทำแล็บควบคู่ไปกับการอ่าน Harris&Harris ได้
เมื่อทำแล็บและอ่านหนังสือจบ ก็ถึงเวลาจะเริ่มโปรเจกต์ของตัวเอง
อนึ่ง HackerMojo มีการพบปะรายสัปดาห์ด้วย และแม้ไม่ได้อยู่ใน Valley ก็เข้าร่วมผ่าน Zoom ได้
เริ่มจากตรงนั้นได้เลย
จริง ๆ แล้วฮาร์ดแวร์ก็เป็นส่วนที่ง่าย และในแง่การผลิตก็เป็นส่วนที่ยากด้วย
alwaysแบบลำดับตรงนี้ถึงใช้ตัวดำเนินการ การกำหนดค่าแบบ non-blocking ปนกับ การกำหนดค่าแบบ blocking?เคยมีเว็บชื่อ opencores ที่รวมโปรเจกต์ HDL โอเพนซอร์สหลาย ๆ อันไว้
สงสัยว่าสมัยนี้มี simulator HDL แบบกระจายขนาดใหญ่ระดับ HPC ที่ดี ๆ บ้างไหม
การใช้ GPU สมัยใหม่กับ simulation ระดับ RTL ดูสมเหตุสมผล
หรือว่าไม่ใช่เว็บเดียวกัน แต่เป็นเว็บอื่นที่คล้ายกัน?
ในที่อย่าง CUDA core สมัยใหม่ ปกติแล้วมีการหารเป็นคำสั่งจริง ๆ ไหม หรือโดยทั่วไป emulate ด้วยซอฟต์แวร์?
วงจรหารในฮาร์ดแวร์จริงกินพื้นที่มหาศาล เลยไม่คิดว่ามันจะอยู่ใน ALU ของ GPU
ใน Verilog การเขียนแค่บรรทัดเดียวว่า
DIV: begin alu_out_reg <= rs / rt; endนั้นง่ายมาก แต่บรรทัดเดียวนั้นกินซิลิคอนไปเยอะถ้าแค่ simulate Verilog อย่างเดียว ก็อาจมองไม่เห็นข้อเท็จจริงนี้
โปรเจกต์หยุดอยู่ที่ simulation และถ้าจะทำเป็นฮาร์ดแวร์จริงยังต้องทำงานอีกมาก
ส่วนตัวคิดว่าของแบบนี้ควรเรียกด้วยชื่ออื่น
ช่องว่างระหว่างสองอย่างนี้แคบลงเรื่อย ๆ และทั้งสองฝ่ายต่างก็เพิ่มความสามารถของอีกฝ่ายเข้าไป แต่ก็ยังมีความแตกต่างค่อนข้างมาก
ผมคิดว่ามันเกี่ยวข้องกับ กฎของ Amdahl [0]
ในความหมายนี้ CPU อาจเรียกได้ว่าเป็นโปรเซสเซอร์ที่ปรับให้เหมาะกับ latency ส่วน GPU เป็นโปรเซสเซอร์ที่ปรับให้เหมาะกับ throughput
ถ้าเจาะจงกว่านั้น [1] ก็อาจเรียก CPU ว่าเป็นโปรเซสเซอร์แบบ data dependency ที่ยาวและลึก และเรียก GPU ว่าเป็นโปรเซสเซอร์แบบ data dependency ที่กว้างและแบน
[0]: https://en.wikipedia.org/wiki/Amdahl%27s_law
[1]: https://en.wikipedia.org/wiki/Data_dependency
เทนเซอร์ก็คืออาร์เรย์ n มิติ
สามารถวางซอฟต์แวร์หรือเฟิร์มแวร์ทับลงไปเพื่อให้ทำงานเหมือน GPU ได้
พยายามปะติดปะต่อจากซอร์สของ EDK2 แล้ว แต่ไม่ชัดเจนว่าส่วนไหนเฉพาะกับ QEMU มากน้อยแค่ไหน
tiny-gpuสมมติว่าเธรดทั้งหมดจะ “บรรจบ” กลับมาที่ program counter เดียวกันหลังแต่ละคำสั่ง เป็นการลดทอนที่ใสซื่อเกินไปมากใน GPU จริง เธรดแต่ละตัวสามารถ branch ไปยัง PC ที่ต่างกันได้ และจะเกิด branch divergence ที่กลุ่มเธรดซึ่งตอนแรกประมวลผลร่วมกันแยกออกไป execute ต่างหาก
ก่อนจะทำ GPU เป็นซิลิคอน น่าจะลองเขียนโปรแกรม GPU ก่อน
แถมจะเรียกว่า SIMD ก็ยังรู้สึกไม่ค่อยตรง
คนนี้คือคนเดียวกับที่เมื่อก่อนเอาวงจรของคนอื่นมาต่อ ๆ กันให้ LED กะพริบ แล้วบอกว่าตัวเองทำ CPU นั่นแหละ
__syncthreads()ทุกครั้งที่ execute เลยไม่ใช่หรือ?