পাঠ ১৪ · ৫৭-এর মধ্যে · মডিউল ৩
Home / Courses / Computer Architecture & Digital Logic / ফ্লিপ-ফ্লপ

ফ্লিপ-ফ্লপ — D, JK, T ফ্লিপ-ফ্লপ

Flip-flops — D, JK & T
৯ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • Level-triggered ল্যাচ ও edge-triggered ফ্লিপ-ফ্লপের নির্ভুল পার্থক্য এবং কেন এই পার্থক্য সিঙ্ক্রোনাইজড সিস্টেমে গুরুত্বপূর্ণ
  • D, JK, T — তিন ধরনের ফ্লিপ-ফ্লপের ইনপুট-আউটপুট আচরণ
  • JK ফ্লিপ-ফ্লপের টগল বৈশিষ্ট্য কীভাবে SR-এর অবৈধ অবস্থাকে অর্থবহ করে তোলে
  • Python-এ clock_tick() মেথড দিয়ে এজ-ট্রিগারিং সিমুলেট করা এবং একটি বাস্তব সিকোয়েন্স ট্রেস করে টগল আচরণ যাচাই

১ · ল্যাচ বনাম ফ্লিপ-ফ্লপ — level-triggered বনাম edge-triggered

L13-এর SR ও D ল্যাচ দুটোই level-triggered — যতক্ষণ Enable সিগন্যাল উঁচু (1) থাকে, ততক্ষণ আউটপুট ইনপুটের যেকোনো পরিবর্তনে সাড়া দিতে থাকে। এটা ছোট, বিচ্ছিন্ন সার্কিটে সমস্যা না হলেও, যখন একগুচ্ছ মেমরি এলিমেন্ট একসাথে সিঙ্ক্রোনাইজড হয়ে কাজ করতে হয় (যেমন CPU-এর ভেতরের শত শত রেজিস্টার), তখন level-triggered আচরণ টাইমিং রেস তৈরি করতে পারে — একটা ল্যাচের আউটপুট এখনো বদলাতে বদলাতেই আরেকটা ল্যাচ সেই অস্থির মান পড়ে ফেলতে পারে।

ফ্লিপ-ফ্লপFlip-Flopএজ-ট্রিগারড মেমরি এলিমেন্ট — শুধুমাত্র ক্লক সিগন্যালের একটি নির্দিষ্ট ট্রানজিশন মুহূর্তে (সাধারণত 0→1 রাইজিং এজ) আউটপুট আপডেট হয়। এই সমস্যার সমাধান দেয় — এটা edge-triggered: আউটপুট শুধু তখনই বদলায় যখন ক্লক সিগন্যাল ঠিক 0 থেকে 1-এ ট্রানজিশন করে (রাইজিং এজ), তার বাইরে যতই ইনপুট বদলাক না কেন, আউটপুট স্থির থাকে। এই "শুধু একটা নির্দিষ্ট মুহূর্তে সাড়া দেওয়া" বৈশিষ্ট্যটাই একগুচ্ছ ফ্লিপ-ফ্লপকে একই ক্লক সিগন্যালে একসাথে, নির্ভরযোগ্যভাবে আপডেট হতে দেয় — সরাসরি M6-এর CPU ডেটাপাথ ও M7-এর পাইপলাইন স্টেজের ভিত্তি, যেগুলো পুরোপুরি ক্লকড ফ্লিপ-ফ্লপ রেজিস্টার দিয়ে তৈরি।

ল্যাচ (level-triggered) -- Enable=1 থাকা পুরো সময় জুড়ে D-এর প্রতিটি পরিবর্তনে Q সাড়া দেয় ফ্লিপ-ফ্লপ (edge-triggered) -- শুধু ক্লকের 0→1 রাইজিং এজ মুহূর্তেই Q একবার আপডেট হয় ফলাফল: একগুচ্ছ ফ্লিপ-ফ্লপ একই ক্লকে একসাথে, নিরাপদে আপডেট হয় -- রেস কন্ডিশন ছাড়াই
এই "শুধু এক মুহূর্তে সাড়া দেওয়া" আচরণই M6-M7-এর সিঙ্ক্রোনাইজড ডেটাপাথ ও পাইপলাইন ডিজাইনকে সম্ভব করে।

২ · D ফ্লিপ-ফ্লপ

সবচেয়ে সরল ও সবচেয়ে বেশি ব্যবহৃত — প্রতিটি ক্লক এজে Q ঠিক সেই মুহূর্তের D-এর মান গ্রহণ করে। এটাই L15-এর রেজিস্টারের মূল বিল্ডিং ব্লক — একটা N-বিট রেজিস্টার আসলে N-টা D ফ্লিপ-ফ্লপ একই ক্লক শেয়ার করে পাশাপাশি বসানো।

৩ · JK ফ্লিপ-ফ্লপ — SR-এর অবৈধ অবস্থার সমাধান, ভিন্নভাবে

JK ফ্লিপ-ফ্লপJK Flip-Flopদুটি ইনপুট J (set) ও K (reset) নিয়ে কাজ করা ফ্লিপ-ফ্লপ, যেখানে J=K=1 হলে Q টগল করে — SR ল্যাচের অবৈধ অবস্থার জন্য একটা সুনির্দিষ্ট, দরকারি আচরণ সংজ্ঞায়িত করে দেয়। SR ল্যাচের মতোই দুটি ইনপুট নেয় — J (Set-এর মতো) ও K (Reset-এর মতো)। J=K=0 হলে Hold, J=1,K=0 হলে Set (Q=1), J=0,K=1 হলে Reset (Q=0) — এখন পর্যন্ত সবই SR-এর মতোই। কিন্তু আসল পার্থক্য J=K=1 হলে — SR ল্যাচে এটা ছিল অবৈধ, JK ফ্লিপ-ফ্লপে এটা একটা সংজ্ঞায়িত, দরকারি আচরণ: Q তার বিপরীত মানে টগল করে (0 হলে 1, 1 হলে 0)। এইভাবে JK ফ্লিপ-ফ্লপ SR-এর "সমস্যার" কেসটাকেই একটা নতুন, দরকারি বৈশিষ্ট্যে রূপান্তরিত করে ফেলে।

৪ · T ফ্লিপ-ফ্লপ — টগল ফ্লিপ-ফ্লপ

T ফ্লিপ-ফ্লপT Flip-Flop (Toggle)JK ফ্লিপ-ফ্লপের সরলীকৃত সংস্করণ, যেখানে J ও K একটি একক ইনপুট T-তে বাঁধা — T=1 হলে টগল, T=0 হলে hold। হলো JK ফ্লিপ-ফ্লপেরই একটা সরলীকৃত সংস্করণ — J ও K-কে একসাথে বেঁধে একটাই ইনপুট T বানানো হয়েছে (J=K=T)। T=0 হলে Hold, T=1 হলে Toggle — ব্যস, এই দুটোই এর একমাত্র আচরণ। এই সরলতাই T ফ্লিপ-ফ্লপকে কাউন্টার বানানোর আদর্শ উপাদান করে তোলে (L16-এ দেখবেন) — একটা বাইনারি কাউন্টারের প্রতিটা বিট আসলে একটা T ফ্লিপ-ফ্লপ, যেটা নির্দিষ্ট শর্তে ক্রমাগত টগল করেই গণনা বাড়ায়।

D
Q = D, প্রতি এজে
JK
Hold/Set/Reset/Toggle — চারটাই সংজ্ঞায়িত
T
শুধু Hold/Toggle — কাউন্টারের ভিত্তি

৫ · কোড সেলে যাচাই — এজ-ট্রিগারিং ও টগল আচরণ

প্রতিটি ক্লাসের clock_tick() মেথডই একমাত্র জায়গা যেখানে .q বদলাতে পারে — মেথডটা কল হওয়া মানেই একটা রাইজিং ক্লক এজ ঘটেছে বলে ধরে নেওয়া হচ্ছে, ঠিক যেমন বাস্তব হার্ডওয়্যারে শুধু এজ মুহূর্তেই আউটপুট আপডেট হয়।

Python
class DFlipFlop:
    """edge-triggered -- .q শুধুমাত্র clock_tick() কল হলেই আপডেট হয় (প্রতিটি কল = একটি রাইজিং এজ)"""
    def __init__(self, initial_q=0):
        self.q = initial_q

    def clock_tick(self, d):
        self.q = d
        return self.q


class JKFlipFlop:
    """J=K=0 hold, J=1,K=0 set, J=0,K=1 reset, J=K=1 toggle"""
    def __init__(self, initial_q=0):
        self.q = initial_q

    def clock_tick(self, j, k):
        if j == 0 and k == 0:
            pass                      # Hold
        elif j == 1 and k == 0:
            self.q = 1                # Set
        elif j == 0 and k == 1:
            self.q = 0                # Reset
        else:                         # j == 1 and k == 1
            self.q = 1 - self.q       # Toggle
        return self.q


class TFlipFlop:
    """JK-এর J=K=T সংস্করণ -- T=1 হলে toggle, T=0 হলে hold"""
    def __init__(self, initial_q=0):
        self.q = initial_q

    def clock_tick(self, t):
        if t == 1:
            self.q = 1 - self.q       # Toggle
        return self.q                 # t == 0 -> Hold


print("D ফ্লিপ-ফ্লপ -- প্রতিটি clock_tick()-এ Q = D")
print("-" * 60)
d_ff = DFlipFlop()
for i, d in enumerate([1, 1, 0, 1, 0], start=1):
    before = d_ff.q
    after = d_ff.clock_tick(d)
    print(f"টিক {i}: D={d} | আগে Q={before} -> পরে Q={after}")

print()
print("JK ফ্লিপ-ফ্লপ -- J=K=1 হলে প্রতি টিকে Q টগল করে")
print("-" * 60)
jk = JKFlipFlop(initial_q=0)
jk_sequence = [(1, 0, "Set"), (0, 0, "Hold"), (1, 1, "Toggle"), (1, 1, "Toggle"), (1, 1, "Toggle"), (0, 1, "Reset")]
for j, k, label in jk_sequence:
    before = jk.q
    after = jk.clock_tick(j, k)
    print(f"টিক: J={j} K={k} ({label:6s}) | আগে Q={before} -> পরে Q={after}")

print()
print("T ফ্লিপ-ফ্লপ -- T=1 হলে টগল, T=0 হলে hold (JK-এর J=K=T সংস্করণ)")
print("-" * 60)
t_ff = TFlipFlop(initial_q=0)
for i, t in enumerate([1, 1, 0, 1, 1, 1], start=1):
    before = t_ff.q
    after = t_ff.clock_tick(t)
    print(f"টিক {i}: T={t} | আগে Q={before} -> পরে Q={after}")

    
JK ফ্লিপ-ফ্লপের ট্রেসে তিনবার পরপর J=K=1 দেওয়া হয়েছে — লক্ষ্য করুন Q প্রতিবার উল্টে যাচ্ছে: 1 → 0 → 1 → 0। এটাই টগল আচরণের প্রকৃত প্রমাণ — কোনো হার্ডকোড করা মান নয়, প্রতিবার 1 - self.q গণনা করেই এই প্যাটার্ন তৈরি হচ্ছে।
মূল কথা · Key takeaway

ফ্লিপ-ফ্লপ ল্যাচের level-triggered সমস্যা সমাধান করে edge-triggered আচরণ দিয়ে — এটাই সিঙ্ক্রোনাইজড মাল্টি-স্টেজ সিস্টেম বানানোর জন্য অপরিহার্য। D ফ্লিপ-ফ্লপ সরল স্টোরেজের জন্য, JK নমনীয়তার জন্য, আর T তার সরলতার কারণে কাউন্টারের জন্য আদর্শ। পরের পাঠে (L15) দেখবেন কীভাবে একগুচ্ছ D ফ্লিপ-ফ্লপ মিলে একটা সম্পূর্ণ রেজিস্টার তৈরি করে।

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

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

প্র ০১ কোডে clock_tick() মেথড কল করাটাই কি "একটা ক্লক এজ" বোঝাচ্ছে — বাস্তব হার্ডওয়্যারে ক্লক তো একটা অবিচ্ছিন্ন বৈদ্যুতিক সিগন্যাল, মেথড কল না?

ঠিক ধরেছেন — বাস্তবে ক্লক একটা ক্রমাগত দোলায়মান (oscillating) ভোল্টেজ সিগন্যাল, কোনো ফাংশন কল নয়। কিন্তু সিমুলেশনের উদ্দেশ্য হলো ফ্লিপ-ফ্লপের আচরণ সঠিকভাবে মডেল করা — "শুধু একটা নির্দিষ্ট মুহূর্তে একবার আপডেট হওয়া" এই ধারণাটাকেই clock_tick() মেথড ক্যাপচার করছে। প্রতিটা কল ঠিক একটা রাইজিং এজের সমতুল্য বলে ধরে নেওয়া হয়েছে, আর মেথডের বাইরে .q কখনো নিজে থেকে বদলায় না — এটাই edge-triggered-এর মূল বৈশিষ্ট্য, শুধু বাস্তব ভোল্টেজ তরঙ্গের বদলে একটা বিচ্ছিন্ন (discrete) কলে প্রকাশ করা হয়েছে।

প্র ০২ JK ফ্লিপ-ফ্লপ থাকতে T ফ্লিপ-ফ্লপ আলাদা করে দরকার কেন — J=K=1 বেঁধে দিলেই তো একই কাজ হয়?

ঠিকই বলেছেন যে গাণিতিকভাবে T ফ্লিপ-ফ্লপ শুধু JK-এর J=K একসাথে বাঁধা সংস্করণ — কিন্তু কাউন্টারের মতো সার্কিটে যেখানে প্রতিটা বিটকে শুধু "toggle" আচরণই দরকার (Set/Reset/Hold-এর আলাদা নিয়ন্ত্রণ কখনো লাগে না), সেখানে দুটো আলাদা ইনপুট (J, K) না রেখে একটাই ইনপুট (T) রাখলে সার্কিট ডিজাইন সরল হয়, কম ওয়্যারিং লাগে, আর উদ্দেশ্যও পরিষ্কার হয়ে যায়। এটা ঠিক NAND-কেই AND/OR/NOT বানানো সম্ভব হলেও (L07) বাস্তবে আলাদা AND/OR গেট রাখার মতো ব্যবহারিক সুবিধার প্রশ্ন — গাণিতিক প্রয়োজনীয়তা নয়, ডিজাইন-স্বচ্ছতার প্রশ্ন।

প্র ০৩ যদি ফ্লিপ-ফ্লপ শুধু ক্লক এজেই সাড়া দেয়, তাহলে দুই ক্লক এজের মাঝখানে D/J/K/T ইনপুট যতই বদলাক, তার কোনো প্রভাবই কি পড়ে না?

সঠিক — এবং এটাই ফ্লিপ-ফ্লপের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য। দুই ক্লক এজের মাঝখানে ইনপুট যতই ওঠানামা করুক (যেমন সিগন্যাল নয়েজের কারণে), ফ্লিপ-ফ্লপ শুধু পরবর্তী রাইজিং এজ মুহূর্তে ইনপুটের যে মান "দেখে" সেটাই গ্রহণ করে — তার আগের সব ওঠানামা উপেক্ষা করে। কোডেও এটাই প্রতিফলিত হয়েছে: clock_tick() কল করার আগে যতবার ইচ্ছা d, j, k, বা t-এর মান নিয়ে চিন্তা করা যায়, কিন্তু .q শুধু মেথড কলের সময় দেওয়া চূড়ান্ত মান অনুযায়ীই একবার আপডেট হয়।

অনুশীলন

  1. হাতে ট্রেস করুন: একটি JK ফ্লিপ-ফ্লপ Q=0 দিয়ে শুরু হয়। এই সিকোয়েন্সের প্রতিটি টিকের পর Q-এর মান হাতে লিখুন: (J=1,K=1), (J=1,K=1), (J=0,K=0), (J=1,K=1)।

    টিক ১ (J=K=1, Toggle): Q 0 থেকে 1 হয়। টিক ২ (J=K=1, Toggle): Q 1 থেকে 0 হয়। টিক ৩ (J=0,K=0, Hold): Q 0-ই থাকে। টিক ৪ (J=K=1, Toggle): Q 0 থেকে 1 হয়। চূড়ান্ত মান: Q=1। লক্ষ্য করুন Hold ধাপটা টগলের গণনায় বাধা দেয় না — শুধু সেই একটা টিকের জন্য পরিবর্তন থামিয়ে দেয়, তারপরের টগল আবার ঠিক আগের মান থেকেই উল্টে যায়।

  2. চিন্তা করুন: একটি T ফ্লিপ-ফ্লপে যদি প্রতিটা ক্লক টিকেই T=1 দেওয়া হয় (কখনো T=0 না দিয়ে), তাহলে ১০ বার টিক করার পর Q-এর মান কত হবে (শুরুর মান Q=0 ধরে), এবং কেন?

    প্রতিটা টিকে Q উল্টে যাবে, তাই মানের প্যাটার্ন হবে: 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 — অর্থাৎ জোড় সংখ্যক টিকের পর Q সবসময় তার শুরুর মানে (0) ফিরে আসবে, বিজোড় সংখ্যক টিকের পর বিপরীত মানে (1) থাকবে। যেহেতু ১০ একটা জোড় সংখ্যা, ১০ বার টিকের পর Q=0 হবে — ঠিক শুরুর মানেই। এই "জোড়-বিজোড় টিক সংখ্যায় পর্যায়ক্রমে ফিরে আসা" আচরণটাই পরের পাঠে (L16) বাইনারি কাউন্টারের সবচেয়ে নিচু বিটের (LSB) আচরণ ব্যাখ্যা করবে।

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

আগের পাঠ
ল্যাচ — SR ল্যাচ ও D ল্যাচ