RISC-V অ্যাসেম্বলারে প্রোগ্রামিং শুরু করার জন্য আপনার যা যা প্রয়োজন

  • প্রস্তুত পরিবেশ: ASM অনুশীলনের জন্য জুপিটার, কম্পাইলিংয়ের জন্য riscv32-none-elf এবং সিমুলেশনের জন্য GHDL+GtkWave।
  • RISC-V ABI এবং সঠিক স্ট্যাক হ্যান্ডলিং সহ মাস্টার লুপ, কন্ডিশনাল এবং ফাংশন।
  • পরিবেশ অনুসারে ECALL: জুপিটার (সহজ কোড) বনাম লিনাক্স (a0..a2 এবং a7 সিস্টেম কল সহ)।
  • এবার এগিয়ে যান: C/C++ কে বাইনারিতে কম্পাইল করুন, ROM তৈরি করুন এবং FPGA তে একটি RV32I CPU তে চালান।

RISC-V অ্যাসেম্বলার

যদি আপনি নিম্ন-স্তরের প্রোগ্রামিং সম্পর্কে আগ্রহী হন এবং আধুনিক স্থাপত্যের উপর অ্যাসেম্বলি প্রোগ্রামিং শিখতে চান, তাহলে RISC-V হল সেরা প্রবেশপথগুলির মধ্যে একটি। এই উন্মুক্ত আইএসএ, শিল্প ও শিক্ষাক্ষেত্রে দুর্দান্ত আকর্ষণের সাথে, আপনাকে সাধারণ সিমুলেটর থেকে শুরু করে FPGA তে চালানো পর্যন্ত অনুশীলন করতে দেয়, সম্পূর্ণ টুলচেইনের মাধ্যমে C/C++ কম্পাইল করতে এবং জেনারেট করা ASM পরীক্ষা করতে।

এই ব্যবহারিক নির্দেশিকায় আমি আপনাকে বলছি, ধাপে ধাপে এবং খুব পার্থিব দৃষ্টিভঙ্গির সাথে, RISC-V অ্যাসেম্বলারে প্রোগ্রামিং শুরু করার জন্য আপনার যা যা প্রয়োজন: টুলস, ওয়ার্কফ্লো, মূল উদাহরণ (কন্ডিশনাল, লুপ, ফাংশন, সিস্টেম কল), সাধারণ ল্যাব অনুশীলন, এবং, যদি আপনি এটির জন্য প্রস্তুত হন, তাহলে একটি RV32I CPU কীভাবে বাস্তবায়িত হয় এবং একটি FPGA-সংশ্লেষিত কোরে আপনার নিজস্ব বাইনারি কীভাবে চালানো যায় তা একবার দেখুন।

RISC-V অ্যাসেম্বলার কী এবং এটি মেশিন ভাষার সাথে কীভাবে সম্পর্কিত?

RISC-V একটি ওপেন ইন্সট্রাকশন সেট আর্কিটেকচার (ISA) সংজ্ঞায়িত করে: RV32I বেস রিপারটোয়ারে 39টি নির্দেশাবলী রয়েছে খুবই লম্ব এবং বাস্তবায়ন করা সহজ। অ্যাসেম্বলার (ASM) হল একটি নিম্ন-স্তরের ভাষা যা add, sub, lw, sw, jal ইত্যাদি স্মৃতিবিদ্যা ব্যবহার করে, যা ISA-এর সাথে সামঞ্জস্যপূর্ণ। অন্তর্নিহিত মেশিন কোড হল CPU যে বিটগুলি বোঝে; অ্যাসেম্বলার হল এর মানব-পঠনযোগ্য উপস্থাপনা। যেকোনো উচ্চ-স্তরের ভাষার চেয়ে হার্ডওয়্যারের কাছাকাছি.

যদি আপনি C থেকে আসেন, তাহলে আপনি লক্ষ্য করবেন যে ASM আগের মতো চলছে না: এটি একত্রিত এবং সংযুক্ত করতে হবে। একটি বাইনারি তৈরি করতে। বিনিময়ে, এটি আপনাকে সার্জিক্যাল নির্ভুলতার সাথে রেজিস্টার, অ্যাড্রেসিং মোড এবং সিস্টেম কল নিয়ন্ত্রণ করতে দেয়। এবং যদি আপনি একটি শিক্ষণ সিমুলেটরের সাথে কাজ করেন, তাহলে আপনি "ecall" কে একটি ইনপুট/আউটপুট এবং টার্মিনেশন প্রক্রিয়া হিসেবে দেখতে পাবেন, নির্দিষ্ট নিয়মাবলী সহ পরিবেশের উপর নির্ভর করে (যেমন, জুপিটার বনাম লিনাক্স)।

সরঞ্জাম এবং পরিবেশ: সিমুলেটর, টুলচেইন এবং FPGA

দ্রুত শুরু করার জন্য, জুপিটার গ্রাফিক্স সিমুলেটরটি আদর্শ। এটি একটি অ্যাসেম্বলার/সিমুলেটর যা শিক্ষাদানের জন্য ডিজাইন করা হয়েছে, SPIM/MARS/VENUS দ্বারা অনুপ্রাণিত এবং বিশ্ববিদ্যালয়ের কোর্সে ব্যবহৃত। এটির সাহায্যে, আপনি সম্পূর্ণ টুলচেইনটি শুরু থেকে কনফিগার না করেই RV32I প্রোগ্রাম লিখতে, একত্রিত করতে এবং চালাতে পারবেন।

আপনি যদি আরও এক ধাপ এগিয়ে যেতে চান, তাহলে আপনার বেয়ার-মেটাল টুলচেইনে আগ্রহ থাকতে পারে: riscv32-none-elf (GCC/LLVM) RISC-V বাইনারিগুলিতে C/C++ কম্পাইল করতে, এবং disassembly এর জন্য objdump এর মতো ইউটিলিটি। হার্ডওয়্যার সিমুলেশনের জন্য, GHDL আপনাকে VHDL কম্পাইল করতে, এটি কার্যকর করতে এবং GtkWave এর মাধ্যমে পরিদর্শনের জন্য একটি .ghw ফাইলে সিগন্যাল ডাম্প করতে দেয়। এবং, যদি আপনি প্রকৃত হার্ডওয়্যারের জন্য প্রস্তুত হন, আপনি একটি RV32I CPU কে ​​একটি FPGA তে সংশ্লেষিত করতে পারেন প্রস্তুতকারকের পরিবেশ (যেমন ইন্টেল কোয়ার্টাস) অথবা বিনামূল্যের টুলচেইন সহ।

বৃহস্পতি দিয়ে শুরু করা: মৌলিক প্রবাহ এবং সংযোজক নিয়ম

বৃহস্পতি শেখার বক্ররেখাকে সহজ করে তোলে। আপনি এডিটর ট্যাবে ফাইল তৈরি এবং সম্পাদনা করেন, এবং প্রতিটি প্রোগ্রাম গ্লোবাল __start ট্যাগ দিয়ে শুরু হয়। নিশ্চিত করুন যে আপনি এটি একটি .globl নির্দেশিকা দিয়ে ঘোষণা করেছেন (হ্যাঁ, এটি .globl, .global নয়)। ট্যাগগুলি একটি কোলন দিয়ে শেষ হয় এবং মন্তব্যগুলি # বা ; দিয়ে শুরু হতে পারে।

পরিবেশের কিছু কার্যকর নিয়ম: প্রতি লাইনে একটি করে নির্দেশ, এবং যখন আপনি প্রস্তুত হন, তখন এটি সংরক্ষণ করুন এবং এটি একত্রিত করে চালানোর জন্য F3 টিপুন। প্রোগ্রামগুলি অবশ্যই একটি প্রস্থান কল ecall দিয়ে শেষ হবে; জুপিটারে, 10 থেকে a0 সেট করা প্রোগ্রামের সমাপ্তির সংকেত দেয়, যেমন একটি "প্রস্থান"।

ন্যূনতমভাবে, বৃহস্পতির উপর আপনার ASM কঙ্কালটি দেখতে এরকম হতে পারে, প্রবেশ বিন্দুটি স্পষ্ট এবং ecall দ্বারা সমাপ্তি সহ: এটি বাকি অনুশীলনের ভিত্তি।.

.text
.globl __start
__start:
  li a0, 10     # código 10: terminar
  ecall         # finalizar programa

কলিং কনভেনশন (ABI) এবং স্ট্যাক ম্যানেজমেন্ট

অ্যাসেম্বলারে প্রোগ্রামিং ফাংশনগুলির জন্য নিয়ম মেনে চলা প্রয়োজন: আর্গুমেন্টগুলি সাধারণত a0..a7 এ আসেফলাফল সাধারণত a0 তে ফেরত পাঠানো হয়, এবং কলগুলিকে রিটার্ন ঠিকানা (ra) এবং সংরক্ষিত রেজিস্টার (s0..s11) সংরক্ষণ করতে হবে। এটি করার জন্য, স্ট্যাক (sp) আপনার বন্ধু: এটি প্রবেশের সময় স্থান সংরক্ষণ করে এবং প্রস্থান করার সময় এটি পুনরুদ্ধার করে।

কিছু নির্দেশাবলী যা আপনি সর্বদা ব্যবহার করবেন: তাৎক্ষণিক নম্বর এবং ঠিকানা লোড করতে li এবং la ব্যবহার করুন।, যোগের জন্য add/addi, মেমোরি অ্যাক্সেসের জন্য lw/sw, unconditional j/jal জাম্প করে এবং jr ra রিটার্ন করে, সেইসাথে beq/bne/bge এর মতো শর্তাবলী। এখানে সাধারণ উদাহরণ সহ একটি দ্রুত অনুস্মারক দেওয়া হল:

# cargar inmediato y una dirección
li t1, 5
la t1, foo

# aritmética y actualización de puntero de pila
add t3, t1, t2
addi sp, sp, -8   # reservar 8 bytes en stack
sw ra, 4(sp)      # salvar ra
sw s0, 0(sp)      # salvar s0

# acceso a memoria con base+desplazamiento
lw t1, 8(sp)
sw a0, 8(sp)

# saltos y comparaciones
beq t1, t2, etiqueta
j etiqueta
jal funcion
jr ra

RISC-V-তে একটি ক্লাসিক লুপ স্পষ্টভাবে গঠন করা যেতে পারে, অবস্থা, শরীর এবং ধাপ পৃথককারী. জুপিটারে, আপনি a0-এ লোড করা কোডের উপর ভিত্তি করে ecall দিয়ে মানগুলিও প্রিন্ট করতে পারেন:

.text
.globl __start
__start:
  li t0, 0      # i
  li t1, 10     # max
cond:
  bge t0, t1, endLoop
body:
  mv a1, t0     # pasar i en a1
  li a0, 1      # código ecall para imprimir entero
  ecall
step:
  addi t0, t0, 1
  j cond
endLoop:
  li a0, 10     # código ecall para salir
  ecall

রিকার্সিভ ফাংশনের জন্য, রেজিস্টার এবং ra সংরক্ষণ/পুনরুদ্ধার করার যত্ন নিন। ফ্যাক্টোরিয়াল হল ক্যানোনিকাল উদাহরণ যা আপনাকে স্ট্যাক ফ্রেম এবং সঠিক ঠিকানায় নিয়ন্ত্রণ ফিরিয়ে আনার বিষয়ে ভাবতে বাধ্য করে:

.text
.globl __start
__start:
  li a0, 5          # factorial(5)
  jal factorial
  # ... aquí podrías imprimir a0 ...
  li a0, 10
  ecall

factorial:
  # a0 trae n; ra tiene la dirección de retorno; sp apunta a tope de pila
  bne a0, x0, notZero
  li a0, 1          # factorial(0) = 1
  jr ra
notZero:
  addi sp, sp, -8
  sw s0, 0(sp)
  sw ra, 4(sp)
  mv s0, a0
  addi a0, a0, -1
  jal factorial
  mul a0, a0, s0
  lw s0, 0(sp)
  lw ra, 4(sp)
  addi sp, sp, 8
  jr ra

ইকল সহ ইনপুট/আউটপুট: জুপিটার এবং লিনাক্সের মধ্যে পার্থক্য

পরিবেশ থেকে পরিষেবা আহ্বান করার জন্য ecall নির্দেশ ব্যবহার করা হয়। জুপিটারে, a0-তে সহজ কোড (যেমন, 1টি প্রিন্ট পূর্ণসংখ্যা, 4টি প্রিন্ট স্ট্রিং, 10 প্রস্থান) উপলব্ধ ক্রিয়াকলাপ নিয়ন্ত্রণ করুন। তবে, লিনাক্সে, a0..a2 সাধারণত প্যারামিটার ধারণ করে, a7 হল syscall নম্বর, এবং শব্দার্থবিদ্যা কার্নেল কলের সাথে মিলে যায় (লেখা, প্রস্থান, ইত্যাদি)।

লিনাক্সের জন্য এই "হ্যালো ওয়ার্ল্ড" প্যাটার্নটি চিত্রিত করে: তুমি a0..a2 এবং a7 রেজিস্টারগুলো প্রস্তুত করো এবং আপনি ecall চালান। .global নির্দেশিকা এবং _start এন্ট্রি পয়েন্টটি লক্ষ্য করুন:

# a0-a2: argumentos; a7: número de syscall
.global _start
_start:
  addi a0, x0, 1     # 1 = stdout
  la a1, holamundo   # puntero al mensaje
  addi a2, x0, 11    # longitud
  addi a7, x0, 64    # write
  ecall
  addi a0, x0, 0     # return code 0
  addi a7, x0, 93    # exit
  ecall
.data
holamundo: .ascii "Hola mundo\n"

যদি আপনার লক্ষ্য হয় নিয়ন্ত্রণ যুক্তি, স্মৃতি এবং ফাংশন অনুশীলন করা, বৃহস্পতি আপনাকে তাৎক্ষণিক প্রতিক্রিয়া দেয় এবং অনেক ল্যাবে আপনার সমাধান যাচাই করার জন্য একটি অটোগ্রেডার থাকে। আপনি যদি আসল সিস্টেমের সাথে ইন্টারঅ্যাক্ট করার অনুশীলন করতে চান, তাহলে আপনাকে লিনাক্সের জন্য কম্পাইল করতে হবে এবং কার্নেল সিস্টেমকল ব্যবহার করতে হবে।

শুরু করার অনুশীলনী: শর্তাবলী, লুপ এবং ফাংশন

RISC-V ASM-এ শুরু করার জন্য একটি ক্লাসিক ব্যায়ামের সেট তিনটি স্তম্ভকে অন্তর্ভুক্ত করে: শর্তাবলী, লুপ এবং ফাংশন কল, সঠিক রেজিস্টার এবং স্ট্যাক ব্যবস্থাপনার উপর মনোযোগ দিয়ে:

  • নেতিবাচক: এমন ফাংশন যা সংখ্যাটি ধনাত্মক হলে 0 এবং ঋণাত্মক হলে 1 প্রদান করে। a0 তে আর্গুমেন্ট গ্রহণ করে এবং a0 তে ফেরত দেয়, অ-উদ্বায়ী রেকর্ড ধ্বংস না করে।
  • গুণনীয়ক: একটি সংখ্যার ভাজকগুলির মধ্য দিয়ে লুপ করুন, রানটাইমে সেগুলি মুদ্রণ করুন এবং মোট পরিমাণ ফেরত দিন। তুমি চক্র, বিভাজন/মোড এবং কল টু ইকল অনুশীলন করবে। মুদ্রণ এর জন্য.
  • উপরের: একটি স্ট্রিং-এ একটি পয়েন্টার দেওয়া হলে, এটি অতিক্রম করুন এবং ছোট হাতের অক্ষরকে বড় হাতের অক্ষরে রূপান্তর করুন। একই ঠিকানায় ফিরে যান; যদি আপনি লুপের সময় পয়েন্টারটি সরান, তাহলে ফিরে আসার আগে এটি পুনরায় সেট করুন।

তিনটির জন্যই, এটি প্যারামিটার পাস এবং রিটার্নের নিয়মকে সম্মান করে, এবং প্রোগ্রামটি exit ecall দিয়ে শেষ করে যখন তুমি বৃহস্পতিতে এটি চেষ্টা করবে। এই অনুশীলনগুলি নিয়ন্ত্রণ প্রবাহ, স্মৃতি এবং স্টেটফুল ফাংশনগুলিকে অন্তর্ভুক্ত করে।

আরও গভীরে খনন: RV32I ISA থেকে একটি সংশ্লেষণযোগ্য CPU পর্যন্ত

RISC-V তার উন্মুক্ততার জন্য উল্লেখযোগ্য: যে কেউ একটি RV32I কোর বাস্তবায়ন করতে পারে। এমন শিক্ষামূলক নকশা রয়েছে যা ধাপে ধাপে দেখায় যে কীভাবে বাস্তব-বিশ্বের প্রোগ্রামগুলি পরিচালনা করে এমন একটি বেস সিপিইউ তৈরি করতে হয়, riscv32-none-elf-এর জন্য GCC/LLVM দিয়ে কম্পাইল করা হয়েছেআপনার অ্যাসেম্বলার চালানোর সময় "গভীর" কী ঘটে সে সম্পর্কে অভিজ্ঞতা আপনাকে অনেক কিছু শেখায়।

সাধারণ বাস্তবায়নে একটি মেমরি কন্ট্রোলার থাকে যা ROM এবং RAM কে বিমূর্ত করে, মূলের সাথে আন্তঃসংযুক্ত। ঐ কন্ট্রোলারের ইন্টারফেসে সাধারণত থাকে:

  • AddressIn (32 বিট): অ্যাক্সেস করার ঠিকানা। অ্যাক্সেসের উৎস নির্ধারণ করে নির্দেশ বা তথ্যের।
  • DataIn (৩২ বিট): যে ডেটা লিখতে হবে। অর্ধশব্দের জন্য, মাত্র ১৬টি LSB বিট ব্যবহার করা হয়; বাইটের জন্য, ৮টি LSB বিট ব্যবহার করা হয়। পড়ার সময় উপেক্ষা করা হয়েছে.
  • প্রস্থ: ০=বাইট, ১=অর্ধেক শব্দ (১৬ বিট), ২ অথবা ৩=শব্দ (৩২ বিট)। আকার নিয়ন্ত্রণ.
  • ExtendSignIn: ৮/১৬ বিট পড়ার সময় সাইন ইন DataOut প্রসারিত করা হবে কিনা। লেখায় এটি উপেক্ষা করা হয়.
  • WEIn: 0=পড়ুন, 1=লেখুন। প্রবেশের দিকনির্দেশনা.
  • StartIn: start edge; এটিকে 1 এ সেট করলে লেনদেন শুরু হয়, ঘড়ির সাথে সিঙ্ক্রোনাইজ করা হয়েছে.

যখন ReadyOut=1, তখন অপারেশনটি সম্পূর্ণ হয়: পড়ার সময়, DataOut-এ ডেটা থাকে (প্রযোজ্য ক্ষেত্রে সাইন এক্সটেনশন সহ); লেখার সময়, ডেটা ইতিমধ্যেই মেমরিতে থাকে। এই স্তরটি আপনাকে কোর স্পর্শ না করেই অভ্যন্তরীণ FPGA RAM, SDRAM, অথবা বহিরাগত PSRAM অদলবদল করতে দেয়।

একটি সরল শিক্ষাদান সংস্থা তিনটি VHDL উৎস সংজ্ঞায়িত করে: ROM.vhd (৪ কিলোবাইট), RAM.vhd (৪ কিলোবাইট) এবং Memory.vhd (৮ কিলোবাইট) যা একটি সংলগ্ন স্থান (0x0000..0x0FFF এ ROM, 0x1001..0x1FFF এ RAM) এবং 0x1000 (পিনের বিট 0) এ ম্যাপ করা একটি GPIO উভয়ের সাথেই একীভূত হয়। MemoryController.vhd কন্ট্রোলার "Memory" চালু করে এবং কার্নেলে ইন্টারফেস প্রদান করে।

কোর সম্পর্কে: সিপিইউতে ৩২টি ৩২-বিট রেজিস্টার (x0..x31) রয়েছে, যার x0 শূন্যের সাথে সংযুক্ত এবং লেখার যোগ্য নয়। ভিএইচডিএল-এ অ্যারে দিয়ে মডেল করা এবং ব্লক তৈরি করা সাধারণ। হাত দিয়ে লজিকের প্রতিলিপি তৈরি এড়াতে, এবং ALU থেকে কোন রেজিস্টার আউটপুট গ্রহণ করবে তা নির্বাচন করার জন্য একটি 5-থেকে-32 ডিকোডার।

ALU একটি নির্বাচক (ALUSel) এর সাথে সম্মিলিতভাবে প্রয়োগ করা হয় যোগ, বিয়োগ, XOR, OR, AND, এর মতো ক্রিয়াকলাপের জন্য। স্থানচ্যুতি (SLL, SRL, SRA) এবং তুলনা (LT, LTU, EQ, GE, GEU, NE)। FPGA গুলিতে LUT সংরক্ষণ করার জন্য, একটি জনপ্রিয় কৌশল হল 1-বিট শিফট বাস্তবায়ন করা এবং স্টেট মেশিন ব্যবহার করে N চক্র পুনরাবৃত্তি করা; এটি ল্যাটেন্সি বৃদ্ধি করে, কিন্তু সম্পদের ব্যবহার হ্রাস পায়.

ALU ইনপুট (ALUIn1/2 এবং ALUSel), গন্তব্য নিবন্ধন নির্বাচন (RegSelForALUOut) এর জন্য মাল্টিপ্লেক্সার দিয়ে নিয়ন্ত্রণ স্পষ্ট করা হয়েছে, মেমরি কন্ট্রোলারের কাছে সংকেত (MCWidthIn, MCAddressIn, MCStartIn, MCWEIn, MCExtendSignIn, MCDataIn), এবং বিশেষ রেজিস্টার PC, IR, এবং শিফট গণনার জন্য একটি কাউন্টার। এই সমস্ত কিছুই ~২৩ টি স্টেট সহ একটি স্টেট মেশিন দ্বারা নিয়ন্ত্রিত হয়।

এই FSM-এর একটি মূল ধারণা হল "বিলম্বিত লোডিং": একটি MUX ইনপুট নির্বাচনের প্রভাব পরবর্তী ঘড়ির প্রান্তে বাস্তবায়িত হয়উদাহরণস্বরূপ, মেমরি থেকে আসা নির্দেশাবলীর সাথে IR লোড করার সময়, ক্রমটি fetch (PC-তে একটি পঠন চালু করা), ReadyOut-এর জন্য অপেক্ষা করা, DataOut-কে IR-তে স্থানান্তর করা এবং পরবর্তী চক্রে, ডিকোডিং এবং সম্পাদন করার অবস্থার মধ্য দিয়ে যায়।

সাধারণ ফেচ পাথ: রিসেটে আপনি PC=RESET_VECTOR (0x00000000) জোর করেন, তারপর আপনি ড্রাইভারটিকে PC ঠিকানায় 4 বাইট পড়ার জন্য কনফিগার করেন, রেডিআউটের জন্য অপেক্ষা করা হচ্ছে এবং আইআর লোড করা হচ্ছেসেখান থেকে, বিভিন্ন রাজ্য একক-চক্র ALU, বহু-চক্র শিফট, লোড/স্টোর, শাখা, জাম্প এবং "স্পেশাল" পরিচালনা করে (একটি শিক্ষণ বাস্তবায়নের ফলে ইচ্ছাকৃতভাবে প্রসেসর বন্ধ করে দিতে পারে)।

আসল কোড কম্পাইল করুন এবং আপনার RISC-V তে এটি চালান।

একটি অত্যন্ত শিক্ষামূলক "ধারণার প্রমাণ" রুট হল riscv32-none-elf ক্রস কম্পাইলার দিয়ে একটি C/C++ প্রোগ্রাম কম্পাইল করা, বাইনারি তৈরি করুন এবং এটি একটি VHDL ROM-এ ডাম্প করুন।। তারপর আপনি GHDL-এ সিমুলেট করেন এবং GtkWave-এ সিগন্যাল বিশ্লেষণ করেন; যদি সবকিছু ঠিকঠাক হয়, তাহলে আপনি একটি FPGA-তে সিন্থেসিস করেন এবং সিলিকনে চলমান সিস্টেমটি দেখতে পান।

প্রথমে, আপনার মানচিত্রের সাথে অভিযোজিত একটি লিঙ্কার স্ক্রিপ্ট: 0x00000000 থেকে 0x00000FFF পর্যন্ত ROM, 0x00001000 এ GPIO এবং RAM 0x00001001 থেকে 0x00001FFF পর্যন্ত। সহজ করার জন্য, আপনি ROM-এ .text (.startup বিভাগ সহ) এবং RAM-এ .data রাখতে পারেন, যদি আপনি প্রথম সংস্করণটি ছোট রাখতে চান তবে ডেটা ইনিশিয়ালাইজেশন বাদ দিন।

সেই মানচিত্রের সাহায্যে, একটি ন্যূনতম বুটস্ট্র্যাপ রুটিন স্ট্যাকটিকে SRAM-এর শেষে রাখে এবং main ব্যবহার করে; "নগ্ন" হিসেবে চিহ্নিত এবং .startup বিভাগে RESET_VECTOR এ স্থাপন করতে। কম্পাইল করার পর, objdump আপনাকে আপনার CPU যে প্রকৃত ASM চালাবে তা দেখতে দেয় (lui/addi to build sp, jal to main, ইত্যাদি)।

একটি ক্লাসিক ব্লিঙ্কার উদাহরণ হল ম্যাপ করা GPIO-এর বিট 0 টগল করা: সিমুলেটরে ডিবাগ করার জন্য অল্প সময়ের অপেক্ষা (GHDL+GtkWave) এবং, বাস্তব হার্ডওয়্যারে, গণনা বৃদ্ধি করুন যাতে ঝিকিমিকি লক্ষণীয় হয়। Makefile একটি .bin এবং একটি স্ক্রিপ্ট তৈরি করতে পারে যা সেই বাইনারিটিকে ROM initialization.vhd-এ রূপান্তর করে; একবার ইন্টিগ্রেটেড হয়ে গেলে, আপনি সম্পূর্ণ VHDL কম্পাইল করেন, সিমুলেট করেন এবং তারপর সংশ্লেষণ করেন।.

এই শিক্ষাদান পদ্ধতিটি পুরোনো FPGA গুলিতেও কাজ করে (যেমন, একটি Intel Cyclone II), যেখানে প্রস্তাবিত টেমপ্লেট ব্যবহার করে অভ্যন্তরীণ RAM অনুমান করা হয় এবং নকশাটি প্রায় 66% সম্পদ-দক্ষ হতে পারে। শিক্ষাগত সুবিধা বিশাল: দেখুন পিসি কীভাবে অগ্রসর হয়, কীভাবে রিড ট্রিগার করা হয় (mcstartin), ReadyOut ডেটা যাচাই করে, IR নির্দেশাবলী ক্যাপচার করে এবং প্রতিটি লাফ বা লাফ FSM এর মাধ্যমে কীভাবে প্রচারিত হয়।

পঠন, অনুশীলন এবং অটোগ্রেডার: একটি রোডম্যাপ

শিক্ষাক্ষেত্রে, স্পষ্ট লক্ষ্য থাকা সাধারণ: শর্তাবলী এবং লুপ অনুশীলন করুন, নিয়ম অনুসারে ফাংশন লিখুন। এবং মেমরি পরিচালনা করুন। গাইডগুলি সাধারণত টেমপ্লেট, একটি সিমুলেটর (বৃহস্পতি), ইনস্টলেশন নির্দেশাবলী এবং সংশোধনের জন্য একটি অটোগ্রেডার সরবরাহ করে।

আপনার পরিবেশ প্রস্তুত করতে, অনুরোধ করা হলে Github Classroom-এ অ্যাসাইনমেন্ট গ্রহণ করুন, রিপোজিটরিটি ক্লোন করুন এবং Jupiter খুলুন। মনে রাখবেন যে __start অবশ্যই বিশ্বব্যাপী হতে হবে, মন্তব্যগুলি # অথবা ; হতে পারে, প্রতি লাইনে একটি করে নির্দেশনা আছে, এবং আপনাকে ecall দিয়ে শেষ করতে হবে (a0-এ কোড 10)। F3 দিয়ে কম্পাইল করুন এবং পরীক্ষা চালান। যদি এটি বুট না হয়, তাহলে ক্লাসিক প্রতিকার হল মেশিনটি রিবুট করা।

প্রতিটি অনুশীলনের প্রত্যাশিত বিন্যাস সম্পর্কে, অনেক নির্দেশিকা স্ক্রিনশট অন্তর্ভুক্ত করে এবং উল্লেখ করে: উদাহরণস্বরূপ, ফ্যাক্টর স্পেস দ্বারা পৃথক করা ভাজক প্রিন্ট করে এবং গণনা ফেরত পাঠায়; Upper স্ট্রিং এর মধ্য দিয়ে লুপ করা উচিত এবং স্পেস, সংখ্যা বা বিরাম চিহ্ন স্পর্শ না করে শুধুমাত্র ছোট হাতের অক্ষরগুলিকে বড় হাতের অক্ষরে রূপান্তর করা উচিত এবং মূল পয়েন্টারটি ফেরত দেওয়া উচিত।

মূল্যায়ন সাধারণত প্রতি সিরিজে পয়েন্ট বন্টন করে (১০/৪০/৫০) এবং অটোগ্রেডার স্কোর দেখতে আপনি একটি চেক চালাতে পারেন।যখন আপনি সন্তুষ্ট হবেন, তখন যেখানেই নির্দেশিত হোক না কেন, রেপো URL যোগ/কমিট/পুশ করুন এবং আপলোড করুন। এই জীবনচক্র শৃঙ্খলা আপনাকে কঠোর যাচাইকরণ এবং বিতরণে অভ্যস্ত করে তোলে।

শক্তিশালী করার জন্য আরও ব্যায়াম: ফিবোনাচ্চি, হ্যানয় এবং কীবোর্ড পড়া

মৌলিক বিষয়গুলো শেখার পর, আরও তিনটি ক্লাসিক নিয়ে কাজ করুন: ফিবোনাচ্চি.এস, হ্যানয়.এসওয়াই সিসকল.এস (অথবা অন্য কোনও রূপ যা কীবোর্ড থেকে পড়ে এবং একটি স্ট্রিং পুনরাবৃত্তি করে)।

  • ফিবোনাচ্চি: আপনি এটিকে পুনরাবৃত্ত বা পুনরাবৃত্ত করতে পারেন; যদি আপনি এটিকে পুনরাবৃত্ত করেন, খরচ এবং ra/s0 সংরক্ষণের ব্যাপারে সতর্ক থাকুন; পুনরাবৃত্তিমূলক অনুশীলন যা লুপ এবং সংযোজন করে।
  • হ্যানয়: রিকার্সিভ ফাংশনকে ASM তে অনুবাদ করা। কলের মধ্যে প্রসঙ্গ এবং আর্গুমেন্ট সংরক্ষণ করে: সুশৃঙ্খল স্ট্যাক ফ্রেম. ecall দিয়ে “origin → destination” মুভমেন্ট প্রিন্ট করে।
  • পড়ুন এবং পুনরাবৃত্তি করুন: একটি পূর্ণসংখ্যা এবং একটি স্ট্রিং পড়ুন, এবং স্ট্রিংটি N বার মুদ্রণ করুন। বৃহস্পতিতে, উপযুক্ত ই-কল কোড ব্যবহার করুন আপনার অনুশীলনে উপলব্ধ; লিনাক্সে, পঠন/লেখার জন্য a7 এবং a0..a2 প্রস্তুত করুন।

এই অনুশীলনগুলি প্যারামিটার পাসিং, লুপ এবং I/O একত্রিত করে। তারা আপনাকে পরিবেশের সাথে ইন্টারফেস সম্পর্কে ভাবতে বাধ্য করে (জুপিটার বনাম লিনাক্স), এবং ASM গঠন করুন যাতে পঠনযোগ্য এবং রক্ষণাবেক্ষণযোগ্য হয়।

সূক্ষ্ম বাস্তবায়নের বিবরণ: রেজিস্টার, ALU, এবং রাজ্য

RV32I শিক্ষামূলক মূল বিষয়ে ফিরে আসা যাক, প্রোগ্রামিং করার সময় আপনি যা দেখেন তার সাথে হার্ডওয়্যার কীভাবে কাজ করে তার সামঞ্জস্যপূর্ণ কিছু সূক্ষ্ম বিবরণ পর্যালোচনা করা মূল্যবান: ALU অপারেশন টেবিল ALUSel (ADD, SUB, XOR, OR, AND, SLL, SRL, SRA, স্বাক্ষরিত এবং স্বাক্ষরবিহীন তুলনা) দ্বারা নির্বাচিত, ডিফল্ট কেস হিসাবে "পরিচয়", এবং বহু-চক্র শিফট সংগ্রহ করার জন্য একটি কাউন্টার ব্যবহার করার "কৌশল"।

জেনারেটের সাথে লজিক নিবন্ধন করলে একটি 5→32 ডিকোডার তৈরি হয়, এবং RegSelForALUOut=00000 কেসটি কিছুই করে না (x0 লেখা যায় না, এটি সর্বদা শূন্যের সমান)। পিসি, আইআর এবং কাউন্টারের নিজস্ব MUX আছে, যা FSM দ্বারা পরিচালিত: রিসেট, ফেচ, ডিকোড/এক্সিকিউট থেকে (এক-চক্র ALU বা শিফট লুপ), লোড/স্টোর, শর্তসাপেক্ষ শাখা, jal/jalr, এবং ebreak এর মতো বিশেষ।

ডেটা মেমোরি অ্যাক্সেসের ক্ষেত্রে, MUX→কন্ট্রোলার সমন্বয় অপরিহার্য: MCWidthIn (8/16/32 বিট), MCWEIn (R/W), MCAddressIn (রেজিস্টার বা PC থেকে), MCExtendSignIn (সাইন করা LB/LH এর জন্য) এবং MCStartIn। শুধুমাত্র যখন ReadyOut=1 তখনই আপনি DataOut ক্যাপচার করতে পারবেন এবং অগ্রিম অবস্থা। এটি আপনার ASM প্রোগ্রামার মানসিকতাকে টেম্পোরাল হার্ডওয়্যার বাস্তবতার সাথে সামঞ্জস্যপূর্ণ করে।

এই সবকিছুই সিমুলেশনে আপনি যা পর্যবেক্ষণ করেন তার সাথে সরাসরি সংযুক্ত: যতবার পিসি এগিয়ে যায়, একটি নির্দেশিকা পঠন শুরু হয়, MCReadyOut আপনাকে বলে যে আপনি IR লোড করতে পারেন, এবং সেখান থেকে নির্দেশ কার্যকর হয় (যেমন, «lui x2,0x2» তারপর sp প্রস্তুত করতে «addi x2,x2,-4», «jal x1, …» main কল করতে)। GtkWave-এ এটি দেখা খুবই আসক্তিকর।

সম্পদ, নির্ভরতা এবং চূড়ান্ত টিপস

এই অভিজ্ঞতা পুনরুত্পাদন করতে আপনার কয়েকটি নির্ভরতা প্রয়োজন: ভিএইচডিএল কম্পাইল করার জন্য জিএইচডিএল এবং সিগন্যাল বিশ্লেষণের জন্য জিটিকেওয়েভক্রস-কম্পাইলারের জন্য, যেকোনো GCC riscv32-none-elf কাজ করবে (আপনি নিজের কম্পাইল করতে পারেন অথবা একটি পূর্ব-নির্মিত ইনস্টল করতে পারেন)। কার্নেলটিকে FPGA-তে পোর্ট করতে, আপনার প্রস্তুতকারকের পরিবেশ (যেমন, Intel/Altera-তে Quartus) অথবা আপনার ডিভাইসের সাথে সামঞ্জস্যপূর্ণ বিনামূল্যের টুলচেইন ব্যবহার করুন।

এছাড়াও, RISC-V নির্দেশিকা এবং নোটগুলি (যেমন, কীভাবে করবেন এবং গ্রিন কার্ড) পড়া, পরামর্শ নেওয়া মূল্যবান প্রোগ্রামিং বই, এবং অনুশীলন করুন জুপিটার এবং অটোগ্রেডার সহ ল্যাবরেটরিগুলি. একটি রুটিন বজায় রাখুন: পরিকল্পনা করুন, বাস্তবায়ন করুন, এজ কেস দিয়ে পরীক্ষা করুন এবং তারপর বৃহত্তর প্রকল্পগুলিতে একীভূত করুন (যেমন FPGA-তে ব্লিঙ্কার)।

এই সমস্ত তথ্যের সাথে, আপনার কাছে ইতিমধ্যেই শুরু করার জন্য প্রয়োজনীয় জিনিসগুলি রয়েছে: কেন অ্যাসেম্বলার ব্যবহার করা হয় বনাম মেশিন কোড, জুপিটার বা লিনাক্স দিয়ে কীভাবে একটি পরিবেশ সেট আপ করবেন, লুপ প্যাটার্ন, কন্ডিশনাল এবং ফাংশন সঠিক স্ট্যাক হ্যান্ডলিং সহ, এবং প্রতিটি নির্দেশ কার্যকর করার সময় কী ঘটে তা আরও ভালভাবে বোঝার জন্য হার্ডওয়্যার বাস্তবায়নের একটি উইন্ডো।

যদি তুমি করণীয় দ্বারা শেখা পছন্দ করো, তাহলে নেগেটিভ, ফ্যাক্টর এবং আপার দিয়ে শুরু করো, তারপর ফিবোনাচ্চি/হ্যানয় এবং একটি কীবোর্ড-রিডিং প্রোগ্রামে যাও। যখন তুমি আরামদায়ক হবে, তখন একটি সাধারণ C++ কম্পাইল করো, ROM টি VHDL-এ ডাম্প করো, GHDL-এ সিমুলেট করো, এবং তারপর FPGA-তে যাও। এটি কম থেকে বেশির দিকে যাত্রা যেখানে প্রতিটি অংশ পরেরটির সাথে খাপ খায়।, এবং আপনার নিজের কোডটি একটি GPIO সরানো বা একটি LED জ্বলজ্বল করা দেখার তৃপ্তি অমূল্য।

সেরা প্রোগ্রামিং বই
সম্পর্কিত নিবন্ধ:
প্রতিটি প্রোগ্রামিং ভাষার জন্য সেরা প্রোগ্রামিং বই