এমবেডেড সিস্টেমের জন্য CPU/ALU/রেজিস্টার রিক্যাপ
এই পাঠে যা শিখবেন
- CPU-র ফেচ-ডিকোড-এক্সিকিউট চক্রে ALU ও রেজিস্টারের ভূমিকার সংক্ষিপ্ত রিক্যাপ
- মাইক্রোকন্ট্রোলারে রেজিস্টার কেন এত কম ও "মূল্যবান" রিসোর্স
- কেন এমবেডেড C প্রোগ্রামারকে রেজিস্টার-সচেতন কোড লিখতে হয় (M3-এ বিস্তারিত আসবে)
- একটি সত্যিকারের, চলমান সিমুলেশন — একটি ছোট রেজিস্টার ফাইলের উপর ALU অপারেশন
১ · CPU, ALU ও রেজিস্টার — সংক্ষিপ্ত রিক্যাপ
CPU কীভাবে ফেচ-ডিকোড-এক্সিকিউট চক্রে ইনস্ট্রাকশন চালায়, ALU-র ভেতরের ডিজিটাল লজিক, এবং ইনস্ট্রাকশন সেট আর্কিটেকচার (ISA) — এসব গভীরভাবে Computer Architecture & Digital Logic কোর্সে কভার করা হয়েছে। এই পাঠ সেই ভিত্তি ধরে নিয়ে শুধু এমবেডেড প্রেক্ষাপটে গুরুত্বপূর্ণ অংশটুকু সংক্ষেপে রিক্যাপ করছে।
সংক্ষেপে: CPU প্রতিটি ইনস্ট্রাকশন ফেচ করে (মেমরি থেকে আনে), ডিকোড করে (কী করতে হবে বোঝে), তারপর এক্সিকিউট করে। যোগ, বিয়োগ, বিটওয়াইজ AND/OR-এর মতো কাজগুলো ALUArithmetic Logic UnitCPU-র সেই অংশ যা গাণিতিক (যোগ, বিয়োগ) ও লজিক্যাল (AND, OR, XOR) অপারেশন সম্পাদন করে। করে, আর ALU-র ইনপুট/আউটপুট মান সাধারণত রেজিস্টারRegisterCPU-র ভেতরের সবচেয়ে দ্রুততম, ছোট আকারের স্টোরেজ স্লট — RAM-এর চেয়ে বহুগুণ দ্রুত অ্যাক্সেসযোগ্য।-এ রাখা হয়, কারণ রেজিস্টার RAM-এর চেয়ে বহুগুণ দ্রুত অ্যাক্সেসযোগ্য।
২ · এমবেডেড দৃষ্টিকোণ: রেজিস্টার কেন কম ও মূল্যবান
একটি আধুনিক ডেস্কটপ/সার্ভার CPU-তে "রেজিস্টার রিনেমিং" নামক কৌশলের মাধ্যমে সীমিত সংখ্যক আর্কিটেকচারাল রেজিস্টারের পেছনে শত শত ফিজিক্যাল রেজিস্টার লুকানো থাকে — কিন্তু একটি মাইক্রোকন্ট্রোলারে এই বিলাসিতা নেই। উদাহরণস্বরূপ, একটি সাধারণ AVR চিপে ৩২টি ৮-বিট জেনারেল-পারপাস রেজিস্টার (R0-R31) থাকে, আর একটি ARM Cortex-M কোরে মাত্র ১৬টি ৩২-বিট রেজিস্টার (R0-R15, যার মধ্যে কয়েকটি আবার স্পেশাল — স্ট্যাক পয়েন্টার, লিংক রেজিস্টার, প্রোগ্রাম কাউন্টার হিসেবে সংরক্ষিত)।
রেজিস্টার সংখ্যা এত কম হওয়ার ফল হলো — একটি জটিল C ফাংশনে অনেকগুলো ভ্যারিয়েবল থাকলে কম্পাইলার বাধ্য হয়ে কিছু মানকে সাময়িকভাবে SRAM-এ "স্পিল" করে রাখে, যা একটি রেজিস্টার-অ্যাক্সেসের তুলনায় ধীর ও বেশি এনার্জি খরচ করে। তাই এমবেডেড C প্রোগ্রামিং-এ (M3-এ বিস্তারিত) লোকাল ভ্যারিয়েবল সংখ্যা ও লুপের ভেতরের জটিলতা সচেতনভাবে সীমিত রাখা একটি বাস্তব পারফরম্যান্স/পাওয়ার বিবেচনা — ডেস্কটপ প্রোগ্রামিং-এ যা প্রায়ই উপেক্ষা করা যায়।
৩ · একটি সত্যিকারের সিমুলেশন — ৮-রেজিস্টার ALU
নিচে একটি ছোট ALU সিমুলেট করা হয়েছে — একটি বাস্তব মাইক্রোকন্ট্রোলারের মতোই মাত্র ৮টি ৮-বিট রেজিস্টার (R0-R7) নিয়ে। প্রতিটি অপারেশনের ফলাফল $8$-বিট রেঞ্জে মাস্ক করা হয়, কারণ বাস্তব হার্ডওয়্যার রেজিস্টারও একটি নির্দিষ্ট বিট-প্রস্থে সীমাবদ্ধ — অর্থাৎ $R_{dest} = (R_{src1} \text{ op } R_{src2}) \bmod 2^8$।
class TinyALU:
# খুব ছোট একটি এমবেডেড CPU-র ALU + রেজিস্টার ফাইল সিমুলেশন
# বাস্তব মাইক্রোকন্ট্রোলারে রেজিস্টার সংখ্যা সীমিত -- এখানে ৮টি (R0-R7) ধরা হলো
def __init__(self, num_registers=8):
self.registers = [0] * num_registers
def load(self, reg, value):
self.registers[reg] = value & 0xFF # ৮-বিট রেজিস্টার, ওভারফ্লো মাস্ক করা হলো
def add(self, dest, src1, src2):
result = (self.registers[src1] + self.registers[src2]) & 0xFF
self.registers[dest] = result
return result
def sub(self, dest, src1, src2):
result = (self.registers[src1] - self.registers[src2]) & 0xFF
self.registers[dest] = result
return result
def bitwise_and(self, dest, src1, src2):
result = self.registers[src1] & self.registers[src2]
self.registers[dest] = result
return result
def bitwise_or(self, dest, src1, src2):
result = self.registers[src1] | self.registers[src2]
self.registers[dest] = result
return result
def dump(self):
return {f"R{i}": v for i, v in enumerate(self.registers)}
cpu = TinyALU(num_registers=8)
cpu.load(0, 50) # R0 = 50
cpu.load(1, 12) # R1 = 12
cpu.load(2, 0b00001111) # R2 = 15
print("লোডের পর রেজিস্টার অবস্থা:", cpu.dump())
add_result = cpu.add(3, 0, 1) # R3 = R0 + R1
print(f"\nADD R3, R0, R1 -> R3 = {add_result} (রেজিস্টার: {cpu.dump()})")
sub_result = cpu.sub(4, 0, 1) # R4 = R0 - R1
print(f"SUB R4, R0, R1 -> R4 = {sub_result} (রেজিস্টার: {cpu.dump()})")
and_result = cpu.bitwise_and(5, 0, 2) # R5 = R0 & R2
print(f"AND R5, R0, R2 -> R5 = {and_result} (বাইনারি: {format(and_result, '08b')})")
or_result = cpu.bitwise_or(6, 1, 2) # R6 = R1 | R2
print(f"OR R6, R1, R2 -> R6 = {or_result} (বাইনারি: {format(or_result, '08b')})")
print("\nচূড়ান্ত রেজিস্টার ফাইল:", cpu.dump())
print(f"ব্যবহৃত রেজিস্টার সংখ্যা: {len(cpu.registers)} -- একটি ডেস্কটপ CPU-র তুলনায় অনেক কম, তাই "
f"প্রতিটি রেজিস্টার সাবধানে পুনঃব্যবহার করতে হয়।")
AND R5, R0, R2-এর ফলাফল ২ (বাইনারি 00000010), কারণ
$50$ = 00110010 আর $15$ = 00001111-এর বিটওয়াইজ AND শুধু সেই বিট পজিশনে ১
রাখে যেখানে দুটোতেই ১ ছিল। এই একই বিট-লেভেল যুক্তি M3-এর বিট-ম্যানিপুলেশন পাঠগুলোতে (L10) হার্ডওয়্যার
রেজিস্টার কন্ট্রোলের জন্য বারবার ব্যবহৃত হবে।
ALU ও রেজিস্টারের মৌলিক কাজ সব CPU-তেই এক — কিন্তু মাইক্রোকন্ট্রোলারে রেজিস্টার সংখ্যা তীব্রভাবে সীমিত, তাই এমবেডেড প্রোগ্রামিং-এ রেজিস্টার-সচেতনতা একটি বাস্তব ইঞ্জিনিয়ারিং বিবেচনা — শুধু তাত্ত্বিক জ্ঞান নয়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ একটি ডেস্কটপ CPU-তে "রেজিস্টার রিনেমিং" থাকে, কিন্তু একটি সাধারণ মাইক্রোকন্ট্রোলারে থাকে না — এটি কি একটি ডিজাইন সীমাবদ্ধতা, নাকি ইচ্ছাকৃত পছন্দ?
মূলত ইচ্ছাকৃত ট্রেড-অফ। রেজিস্টার রিনেমিং বাস্তবায়ন করতে অতিরিক্ত ট্রানজিস্টর, জটিল কন্ট্রোল লজিক ও বেশি পাওয়ার লাগে — যা একটি ডেস্কটপ CPU-র জন্য যুক্তিসঙ্গত (পারফরম্যান্স অগ্রাধিকার), কিন্তু একটি ছোট, সস্তা, কম-পাওয়ার মাইক্রোকন্ট্রোলারের জন্য অপ্রয়োজনীয় জটিলতা ও খরচ। সরলতাই এখানে ডিজাইন অগ্রাধিকার।
প্র ০২
উপরের কোড সেলে cpu.load() মেথডে value & 0xFF কেন করা হয়েছে?
কারণ প্রতিটি রেজিস্টার একটি নির্দিষ্ট বিট-প্রস্থে সীমাবদ্ধ (এখানে ৮-বিট)। যদি কোনো মান ২৫৫-এর
বেশি লোড করার চেষ্টা করা হয়, & 0xFF সেটিকে সেই ৮-বিট রেঞ্জে (০-২৫৫) মাস্ক করে রাখে —
ঠিক যেভাবে বাস্তব হার্ডওয়্যার রেজিস্টারও তার বিট-প্রস্থের বাইরের কোনো মান "মনে" রাখতে পারে না।
প্র ০৩ যদি একটি এমবেডেড C ফাংশনে ১৫টি স্থানীয় ভ্যারিয়েবল থাকে, কিন্তু চিপে মাত্র ৮টি জেনারেল-পারপাস রেজিস্টার থাকে, তাহলে কী ঘটে বলে আপনার ধারণা?
কম্পাইলার সবগুলো ভ্যারিয়েবলকে একসাথে রেজিস্টারে রাখতে পারবে না — কিছু ভ্যারিয়েবলকে সাময়িকভাবে SRAM-এ "স্পিল" করে রাখতে হবে এবং প্রয়োজনমতো আবার রেজিস্টারে লোড করতে হবে। এই স্পিল/রিলোড অতিরিক্ত মেমরি-অ্যাক্সেস সাইকেল ও পাওয়ার খরচ করে — যা L06-এর মেমরি ম্যাপ ও L09-এর embedded C পাঠে আরও স্পষ্ট হবে।
অনুশীলন
-
চিন্তা করুন: উপরের কোড সেলে
cpu.sub(4, 1, 0)কল করলে (অর্থাৎ R1 - R0, যেখানে R1=12, R0=50) ফলাফল কী হবে বলে আপনার ধারণা? মনে রাখবেন ফলাফল& 0xFF-এ মাস্ক করা হয়।$12 - 50 = -38$, যা সরাসরি ঋণাত্মক। কিন্তু
& 0xFFমাস্কিং পাইথনের নিজস্ব টু'জ-কমপ্লিমেন্ট আচরণের কারণে $-38 \bmod 256 = 218$ ফলাফল দেবে — অর্থাৎ R4 = 218 হবে, ঠিক যেভাবে একটি বাস্তব ৮-বিট হার্ডওয়্যার রেজিস্টারও নেগেটিভ ফলাফলকে "র্যাপ অ্যারাউন্ড" করে আনসাইনড রেঞ্জে দেখায়। -
পরীক্ষা করুন: উপরের কোড সেলে
cpu.load(7, 200)এবংcpu.add(7, 7, 7)যোগ করুন (R7-এ নিজের সাথে নিজেকে যোগ করা) এবং Run চেপে ফলাফল দেখুন।$200 + 200 = 400$, কিন্তু
& 0xFFমাস্কিংয়ের কারণে $400 \bmod 256 = 144$ ফলাফল হবে — R7 = 144। এটি একটি বাস্তব ৮-বিট ওভারফ্লো/র্যাপ-অ্যারাউন্ডের সরাসরি উদাহরণ, যা এমবেডেড C কোডে সাইলেন্টলি ভুল ফলাফল দিতে পারে যদি প্রোগ্রামার রেজিস্টারের বিট-প্রস্থ সম্পর্কে সচেতন না থাকে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Computer Architecture & Digital Logic কোর্স সহোদর কোর্স CPU/ALU/পাইপলাইনিং/রেজিস্টার আর্কিটেকচারের গভীর ভিত্তি সেই কোর্সেই তৈরি হয়েছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Design and Analysis of Algorithms ও আরও অনেক কোর্স — সব এক জায়গায়।