পাঠ ৪৯ · ৫৭-এর মধ্যে · মডিউল ১০

DMA — ডাইরেক্ট মেমরি অ্যাক্সেস

DMA — direct memory access
৮ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • DMA কোন বাস্তব সমস্যার সমাধান করে — এবং কেন সেই সমস্যাটি সত্যিই গুরুতর
  • DMA কন্ট্রোলার কীভাবে CPU-কে বাইপাস করে স্বাধীনভাবে বাস ও মেমরি অ্যাক্সেস করে
  • cycle stealing — DMA আর CPU একই বাস শেয়ার করার বাস্তব ফলাফল
  • CPU-driven বনাম DMA-driven ট্রান্সফারের প্রকৃত সাইকেল-সংখ্যা গণনা করে তুলনা

১ · DMA কোন সমস্যা সমাধান করে

ধরুন একটি ডিস্ক কন্ট্রোলারকে ৫০০ KB ডেটা মেমরিতে লিখতে হবে। DMA ছাড়া, এই কাজটি করতে হলে CPU-কে প্রতিটি বাইট (বা ওয়ার্ড)-এর জন্য আলাদা LOAD (ডিভাইস থেকে পড়া) এবং তারপর STORE (মেমরিতে লেখা) ইনস্ট্রাকশন-জোড়া চালাতে হয় — L27-এর LOAD/STORE ইনস্ট্রাকশনের সরাসরি প্রয়োগ। বড় একটি ট্রান্সফারের জন্য, এটি বিশাল সংখ্যক CPU সাইকেল খরচ করে ফেলে বিশুদ্ধ ডেটা-শাফলিং-এ — সময় যা অন্য কোনো দরকারি কম্পিউটেশনে ব্যয় হতে পারত।

২ · DMA কন্ট্রোলার ও কর্মপ্রণালী

DMA কন্ট্রোলার এই সমস্যার সমাধান — একটি আলাদা হার্ডওয়্যার উপাদান যা সিস্টেম বাস (L47) ও মেমরি অ্যাক্সেস করতে পারে CPU-র থেকে সম্পূর্ণ স্বাধীনভাবে। CPU শুধু একবার DMA কন্ট্রোলারকে কনফিগার করে দেয় — সোর্স অ্যাড্রেস, ডেস্টিনেশন অ্যাড্রেস, ট্রান্সফারের দৈর্ঘ্য — এবং এরপর সম্পূর্ণ ভিন্ন, অসম্পর্কিত ইনস্ট্রাকশন চালাতে থাকে, যখন DMA কন্ট্রোলার ব্যাকগ্রাউন্ডে পুরো বাল্ক ট্রান্সফারটি স্বয়ংসম্পূর্ণভাবে সম্পন্ন করে। CPU-কে ট্রান্সফারের একেবারে শেষে মাত্র একবার ইন্টারাপ্ট (L48) করা হয় — প্রতিটি বাইটের জন্য নয়।

৩ · Cycle Stealing

একটি গুরুত্বপূর্ণ বাস্তব সূক্ষ্মতা: যেহেতু DMA কন্ট্রোলার আর CPU একই সিস্টেম বাস (L47) শেয়ার করে, DMA কন্ট্রোলারকে মাঝে মাঝে কিছু পৃথক বাস-সাইকেল "চুরি" করতে হয় তার ট্রান্সফার সম্পন্ন করার জন্য — এই চুরি করা সাইকেলগুলোতে CPU সাময়িকভাবে থেমে থাকে। তবুও, এটি CPU নিজে প্রতিটি বাইট পুরো LOAD/STORE জোড়া দিয়ে ট্রান্সফার করার চেয়ে বহুগুণ বেশি কার্যকর — নিচের কোড সেলে এই পার্থক্য প্রকৃত সংখ্যায় দেখা যাক।

L47 ও L48-এর সাথে সম্পর্ক

DMA একসাথে দুটি আগের ধারণা ব্যবহার করে — L47-এর শেয়ার্ড সিস্টেম বাস (যেটির ওপর দিয়ে DMA কন্ট্রোলার সাইকেল চুরি করে) এবং L48-এর ইন্টারাপ্ট মেকানিজম (যা দিয়ে CPU-কে ট্রান্সফার শেষে একবার জানানো হয়)।

নিচের কোড সেলে ১০,০০০ বাইটের একটি ট্রান্সফারের জন্য CPU-driven ও DMA-driven পদ্ধতির প্রকৃত সাইকেল-সংখ্যা গণনা করা হলো — CPU নিজে ট্রান্সফার করলে প্রতি বাইটে ২ সাইকেল (LOAD+STORE) লাগে, আর DMA ব্যবহার করলে শুধু একবার সেটআপ খরচ (৫০ সাইকেল) প্লাস প্রতি বাইটে সামান্য "চুরি করা" খরচ — ধরে নেওয়া হয়েছে বাস একবারে ৪ বাইট (একটি word) স্থানান্তর করে, তাই প্রতি ৪ বাইটে মাত্র ১টি বাস-সাইকেল চুরি হয় (অর্থাৎ প্রতি বাইটে ০.২৫ সাইকেল)।

Python
# সিমুলেটেড DMA বনাম CPU-driven ট্রান্সফার খরচ -- বাস্তব হার্ডওয়্যার টাইমিং নয়, ধারণা বোঝানোর সিমুলেশন
def cpu_driven_transfer(n_bytes, cycles_per_byte=2):
    """DMA ছাড়া: প্রতিটি বাইটের জন্য CPU নিজে একটি LOAD + একটি STORE ইনস্ট্রাকশন চালায়"""
    return n_bytes * cycles_per_byte

def dma_driven_transfer(n_bytes, setup_cycles=50, cycles_stolen_per_byte=0.25):
    """DMA সহ: CPU শুধু একবার সেটআপ করে (setup_cycles), তারপর DMA কন্ট্রোলার
    প্রতি ৪ বাইট (word) স্থানান্তরে মাত্র ১টি বাস-সাইকেল 'চুরি' করে (cycles_stolen_per_byte=0.25)"""
    return setup_cycles + n_bytes * cycles_stolen_per_byte

n_bytes = 10_000

cpu_total = cpu_driven_transfer(n_bytes)
dma_total = dma_driven_transfer(n_bytes)
speedup = cpu_total / dma_total

print(f"ট্রান্সফারের আকার          : {n_bytes:,} বাইট")
print(f"CPU-driven মোট সাইকেল      : {cpu_total:,}")
print(f"DMA-driven মোট CPU সাইকেল  : {dma_total:,.1f}")
print(f"CPU সাইকেল সাশ্রয় (speedup) : {speedup:.1f}x")

assert dma_total < cpu_total
print()
print(f"অর্থাৎ DMA ব্যবহার করলে CPU এই ট্রান্সফারের জন্য প্রায় {speedup:.1f} গুণ কম সাইকেল ব্যয় করে -- বাকি সময়টা অন্য দরকারি কাজে ব্যবহার করতে পারে।")

    
লক্ষ্য করুন — CPU-driven পদ্ধতিতে ২০,০০০ সাইকেল লাগে, কিন্তু DMA-driven পদ্ধতিতে মাত্র ২,৫৫০ সাইকেল (যার মধ্যে ৫০টি শুধু সেটআপের জন্য, বাকিটা "চুরি করা" সাইকেল) — প্রায় ৮ গুণ কম। ডেটার আকার যত বড় হবে, এই পার্থক্য তত নাটকীয় হবে, কারণ সেটআপ খরচ (৫০ সাইকেল) একবারই লাগে, কিন্তু CPU-driven-এর খরচ সরাসরি ডেটার আকারের সমানুপাতে বাড়তে থাকে।
মূল কথা · Key takeaway

DMA একটি ক্লাসিক হার্ডওয়্যার-অপটিমাইজেশন উদাহরণ — একটি বিশেষায়িত সহায়ক উপাদান (DMA কন্ট্রোলার) সাধারণ, পুনরাবৃত্তিমূলক কাজ (বড় ডেটা ট্রান্সফার) নিজের কাঁধে তুলে নিয়ে CPU-কে মুক্ত করে দেয় আসল কম্পিউটেশনের জন্য। L47-এর শেয়ার্ড বাস আর L48-এর ইন্টারাপ্ট মেকানিজম — দুটোই এখানে একসাথে কাজ করে এই দক্ষতা অর্জনে। পরের মডিউলে (M11) দেখা যাবে কীভাবে multicore আর SIMD-এর মতো প্যারালাল আর্কিটেকচারও একই মূলনীতি অনুসরণ করে — কাজকে ভাগ করে সমান্তরালভাবে চালানো।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ DMA ব্যবহার করলেও CPU সম্পূর্ণ "মুক্ত" নয় — কেন? cycle stealing ঠিক কী মূল্য দিতে হয় CPU-কে?

DMA কন্ট্রোলার আর CPU একই ভৌত বাস শেয়ার করে (L47) — একই সময়ে দুজনেই বাস ব্যবহার করতে পারে না। তাই DMA যখন একটি বাস-সাইকেল ব্যবহার করে তার ট্রান্সফার এগিয়ে নিতে, CPU যদি ঠিক সেই মুহূর্তে বাস অ্যাক্সেস করতে চায় (যেমন মেমরি থেকে পরবর্তী ইনস্ট্রাকশন ফেচ করতে), তাকে সংক্ষিপ্ত সময়ের জন্য থামতে হয়। এটাই "cycle stealing" — CPU সম্পূর্ণ ব্লক হয় না, কিন্তু মাঝে মাঝে ছোট ছোট বিরতি নিতে বাধ্য হয়।

প্র ০২ উপরের কোড সেলে n_bytes যদি ১০,০০০-এর বদলে ১০০ হতো, তাহলে DMA-এর সুবিধা কি একইভাবে নাটকীয় থাকত? কেন বা কেন নয়?

কম নাটকীয় হতো — কারণ DMA-driven-এর setup_cycles=50 একটি ফিক্সড খরচ, ট্রান্সফারের আকার যাই হোক না কেন। ছোট ট্রান্সফারে (১০০ বাইট) DMA-driven মোট সাইকেল হবে 50 + 100×0.25 = 75, আর CPU-driven হবে 100×2 = 200 — speedup মাত্র ~2.7x, ১০,০০০ বাইটের ~7.8x-এর তুলনায় অনেক কম। এটাই দেখায় কেন DMA বিশেষভাবে বড় ট্রান্সফারে সবচেয়ে কার্যকর — ছোট ট্রান্সফারে সেটআপ খরচ আনুপাতিকভাবে বেশি "ভারী" হয়ে যায়।

প্র ০৩ DMA কন্ট্রোলার নিজেই বাস অ্যাক্সেস করতে পারে বলে, এটি কি সরাসরি মেমরিতে লিখতে পারে যেখানে CPU নিজেও কিছু লিখছে? এখানে কী ধরনের বাস্তব ঝুঁকি থাকতে পারে?

হ্যাঁ, এবং এটি একটি বাস্তব সমন্বয়-ঝুঁকি তৈরি করে — যদি DMA কন্ট্রোলার আর CPU একই সময়ে একই মেমরি অঞ্চলে লেখা/পড়া চালায় সঠিক সমন্বয় (synchronization) ছাড়াই, ডেটা অসামঞ্জস্যপূর্ণ (inconsistent) হয়ে যেতে পারে — যেমন CPU একটি অসম্পূর্ণ ট্রান্সফারের মাঝপথের ডেটা পড়ে ফেলতে পারে। এই কারণেই বাস্তব সিস্টেমে DMA ট্রান্সফার চলাকালীন CPU-কে সেই নির্দিষ্ট মেমরি অঞ্চল স্পর্শ না করার নিয়ম মেনে চলতে হয়, এবং ট্রান্সফার শেষে ইন্টারাপ্ট (L48) দিয়েই CPU-কে জানানো হয় যে ডেটা এখন নিরাপদে ব্যবহারযোগ্য।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে setup_cycles যদি ৫০-এর বদলে ৫০০ হতো (আরও জটিল DMA কনফিগারেশন), speedup-এর ওপর কী প্রভাব পড়বে বলে মনে করেন — হাতে হিসাব করার চেষ্টা করুন তারপর কোড পরিবর্তন করে যাচাই করুন।

    নতুন dma_total = 500 + 10000×0.25 = 500 + 2500 = 3000, আর cpu_total অপরিবর্তিত 20000। নতুন speedup = 20000/3000 ≈ 6.7x — আগের ~7.8x থেকে কমে গেছে, কিন্তু তবুও যথেষ্ট বড় সুবিধা। এটি নিশ্চিত করে যে DMA-এর সুবিধা setup_cycles-এর প্রতি সংবেদনশীল, কিন্তু বড় ট্রান্সফারে এখনও দৃঢ়ভাবে টিকে থাকে।

  2. পরীক্ষা করুন: কোড সেলে একটি লুপ যোগ করুন যা n_bytes-এর বিভিন্ন মান (১০০, ১,০০০, ১০,০০০, ১,০০,০০০) নিয়ে speedup প্রিন্ট করে — প্যাটার্নটি কী দেখায়?

    ডেটার আকার বাড়ার সাথে সাথে speedup ক্রমাগত বাড়তে থাকবে এবং একটি সীমার (limit) কাছাকাছি পৌঁছাবে — কারণ n_bytes অনেক বড় হলে setup_cycles-এর প্রভাব নগণ্য হয়ে যায়, আর speedup প্রায় cycles_per_byte / cycles_stolen_per_byte = 2 / 0.25 = 8x-এর কাছাকাছি স্থির হয়ে যায় — এটাই দেখায় বড় ট্রান্সফারে DMA-এর প্রকৃত, তাত্ত্বিক সর্বোচ্চ সুবিধা।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

পূর্ববর্তী পাঠ
ইন্টারাপ্ট হ্যান্ডলিং — হার্ডওয়্যার পার্সপেক্টিভ