সরাসরি পর্বে যান
পর্ব ১৯ · ৬২-এর মধ্যে · মডিউল ৩

প্রজেক্ট — traffic light আর vending machine controller নকশা

Design Project: a Traffic-Light Controller and a Vending-Machine Controller — from Specification, State Diagram and K-maps to Verilog, Testbench, Timing and FPGA

Taught in Bangla, this lesson works through two complete digital design projects: a traffic-light controller split into a Moore FSM and a timer datapath, taken from requirements through state diagram, state assignment, K-maps and equations to Verilog RTL and a testbench, and a coin-operated vending machine comparing Mealy and Moore, with minimisation, change logic and edge cases, then a design review of verification, timing, resources and FPGA lab steps, all checked with simulators.

এই পর্বে যা শিখবেন

  • একটি অস্পষ্ট চাহিদা (“মোড়ে একটা সিগন্যাল লাগবে”) থেকে পরীক্ষা করা যায় এমন requirement আর ধরে নেওয়া শর্তের তালিকা বানানো
  • Traffic light controller-কে controller (FSM) আর datapath (timer) — এই দুই ভাগে ভেঙে state diagram, state table, encoding, K-map আর সমীকরণ পর্যন্ত নেওয়া
  • Vending machine-এর জন্য Mealy আর Moore-এর তুলনা করে সিদ্ধান্ত নেওয়া, state minimisation, ভাংতির logic আর কঠিন edge case সামলানো
  • Synthesizable Verilog RTL আর testbench-এর কাঠামো লেখা, আর software simulator দিয়ে নিজের সমীকরণ যাচাই করা
  • Design review: যাচাইয়ের পরিকল্পনা, timing হিসাব, resource গণনা, সম্প্রসারণ, আর নকশাটি FPGA ল্যাবে নামানোর ব্যবহারিক ধাপ

১ · প্রজেক্টের পথ — specification থেকে যাচাই করা circuit

এই মডিউলের আগের চারটি পর্বে আপনি sequential লজিকের সব যন্ত্রপাতি হাতে পেয়েছেন: পর্ব ১৫-এ latch আর flip-flop, পর্ব ১৬-এ register আর counter, পর্ব ১৭-এ FSM নকশার পুরো পদ্ধতি (Moore, Mealy, state minimisation, state assignment, one-hot), আর পর্ব ১৮-এ timing, debouncing আর synchroniser. এই পর্বে নতুন কোনো তত্ত্ব নেই। এখানে আমরা সেই যন্ত্রপাতি দিয়ে দুটি আসল জিনিস বানাব — যেভাবে একটি ল্যাব রিপোর্ট বা প্রজেক্ট রিপোর্টে লেখা হয়।

প্রথম প্রজেক্ট একটি traffic light controller: একটি ব্যস্ত প্রধান সড়ক আর একটি পার্শ্ব রাস্তার মোড়ের সিগন্যাল। দ্বিতীয়টি একটি vending machine controller: ৫ আর ১০ টাকা নিয়ে ১৫ টাকার চা বা বিস্কুটের প্যাকেট দেয়, বেশি দিলে ভাংতি ফেরত দেয়। দুটিই finite state machine (FSM): প্রথমটি শেখায় FSM-কে একটি counter-এর সাথে জোড়া, দ্বিতীয়টি টাকার মতো “হারানো চলবে না” জিনিস সামলানোর সতর্কতা।

একটি ব্যস্ত রেস্তোরাঁর রান্নাঘর ভাবুন। হেড শেফ সিদ্ধান্ত নেন: “এখন ভাত চড়ান, ভাত হলে ডাল।” কিন্তু তিনি নিজে ঘড়ি ধরে দাঁড়িয়ে থাকেন না — চুলার পাশে একটি timer আছে, শেফ সেটি চালু করে দেন, আর timer বেজে উঠলে পরের সিদ্ধান্ত নেন। শেফ হলেন controller: কোন অবস্থায় আছি, এরপর কী — শুধু এটুকু ভাবেন। চুলা, timer, কাটাকুটির লোক হলেন datapath: গোনা, মাপা, আসল কাজ। দুই পক্ষের মধ্যে কথা হয় অল্প কয়েকটি সংকেতে — “শুরু” আর “শেষ হয়েছে”। ডিজিটাল নকশায় এই ভাগকে বলে FSMD (FSM with datapath).

একটি প্রজেক্ট নিচের ধাপগুলো ধরে এগোয়, আর প্রতিটি ধাপের সিদ্ধান্ত লিখে রাখা হয়:

  1. চাহিদাR1…R6কী করতে হবে, কী কখনো নয়
  2. State diagramMG → MY …অবস্থা আর তীর
  3. Table → সমীকরণD1 = …encoding, K-map
  4. RTL + testbench.vsimulation
  5. Reviewtiming, LUTFPGA-তে নামানো
প্রতিটি ধাপের সাথে একটি যাচাই

Requirement-এর সাথে লিখুন সেটি কীভাবে পরীক্ষা হবে; diagram-এ দেখুন প্রতিটি state থেকে প্রতিটি input-এর ঠিক একটি তীর আছে কি না; সমীকরণ state table-এর প্রতিটি সারিতে মেলান; RTL-এ testbench চালান; circuit-এ critical path হিসাব করুন। এই পর্বের চারটি JavaScript প্রোগ্রাম ঠিক এই যাচাইগুলোই করে।

২ · প্রজেক্ট A, ধাপ ১ — মোড়টি বুঝে requirement লেখা

ধরুন একটি জেলা শহরের একটি প্রধান সড়ক, যেখানে সারাদিন বাস, ট্রাক আর রিকশা চলে। তার সাথে মিশেছে একটি মহল্লার পার্শ্ব রাস্তা, যেখানে গাড়ি আসে মাঝে মাঝে। এখন সিগন্যালটি একটি সাধারণ timer-এ চলে — পার্শ্ব রাস্তায় কেউ না থাকলেও প্রধান সড়ক বারবার থামে। পৌরসভা চায় “বুদ্ধিমান” সিগন্যাল: পার্শ্ব রাস্তার মুখে মাটির নিচে একটি গাড়ি শনাক্তকারী সেন্সর (যেমন রাস্তার নিচে তারের কুণ্ডলী — inductive loop — বা একটি ক্যামেরা) বসানো হবে। গাড়ি থাকলে সেন্সরের output C = 1.

“বুদ্ধিমান সিগন্যাল” কথাটি পরীক্ষা করা যায় না। তাই প্রথম কাজ এটিকে এমন কয়েকটি বাক্যে ভাঙা, যার প্রতিটির সত্য-মিথ্যা যাচাই করা যায়। এখানে সময় মাপা হবে tick-এ; ১ tick = ১ সেকেন্ড। Simulation সহজ রাখতে প্রধান সড়কের ন্যূনতম সবুজ ৮ tick ধরা হলো; বাস্তবে এটি আরও লম্বা, আর নকশায় এগুলো parameter, তাই বদলানো সহজ।

নম্বরRequirementধরনকীভাবে পরীক্ষা হবে
R1দুই রাস্তায় একসাথে সবুজ (বা সবুজ-হলুদ) কখনো নয়নিরাপত্তাপ্রতিটি tick-এ assertion
R2প্রতিটি বাতি সবুজ → হলুদ → লাল ক্রমে বদলায়; হলুদ ঠিক ৩ tickনিরাপত্তাবাতির ক্রম আর হলুদের দৈর্ঘ্য গোনা
R3প্রধান সড়ক একবার সবুজ হলে অন্তত ৮ tick সবুজ থাকেসময়প্রতিটি শেষ হওয়া সবুজের দৈর্ঘ্য ≥ ৮
R4পার্শ্ব রাস্তা সবুজ পায় শুধু যখন সেখানে গাড়ি অপেক্ষা করছেআচরণগাড়ি নেই এমন test-এ প্রধান সড়ক সবসময় সবুজ
R5পার্শ্ব রাস্তার সবুজ শেষ হয় গাড়ি ফুরালে, অথবা সর্বোচ্চ ৮ tick পরেন্যায্যতালম্বা সারির test-এ সবুজ ৮ tick-এ থামে
R6Reset-এর পরে প্রধান সড়ক সবুজ, পার্শ্ব রাস্তা লালশুরুReset test

লক্ষ করুন R1 আর R2 “কখনো খারাপ কিছু ঘটবে না” ধরনের — এদের বলে safety property. R4–R5 বলে “ভালো কিছু ঘটবেই” — গাড়ি অপেক্ষা করলে একসময় সবুজ পাবেই। নিরাপত্তার শর্ত ভাঙা মানে দুর্ঘটনা, তাই এগুলো পরীক্ষা হবে প্রতিটি tick-এ, কোনো ব্যতিক্রম ছাড়া।

ধরে নেওয়া শর্ত (assumptions)

প্রতিটি নকশা কিছু জিনিস ধরে নেয়। সেগুলো লিখে না রাখলে পরে অন্য কেউ ভুল পরিবেশে circuit বসাতে পারেন। আমাদের তালিকা:

  • A1 — tick আসে একটি ভাগ করা crystal থেকে। ঘড়ির ৩২.৭৬৮ kHz crystal-এর কম্পাঙ্ক ঠিক ২১৫ Hz, তাই ১৫-বিটের একটি binary counter দিয়ে ভাগ করলে প্রতি সেকেন্ডে একটি tick (পর্ব ১৬)। FPGA বোর্ডের দ্রুত clock (যেমন ৫০ MHz) থেকে আমরা নতুন clock বানাব না, বরং প্রতি সেকেন্ডে এক cycle-এর একটি clock enable পালস বানাব (অংশ ৯)।
  • A2 — সেন্সরের সংকেত synchronised. গাড়ি আসে যখন খুশি, clock-এর তালে নয়। এমন asynchronous input সরাসরি flip-flop-এ দিলে metastability-র ঝুঁকি থাকে; তাই C আগে একটি দুই-flip-flop-এর synchroniser পার হয় (পর্ব ১৮)।
  • A3 — পথচারীর বোতাম (ঐচ্ছিক) debounced আর মনে রাখা। যান্ত্রিক বোতাম চাপলে কয়েক মিলিসেকেন্ড ধরে লাফায় (bounce), তাই আগে debouncing (পর্ব ১৮), তারপর একটি “অনুরোধ আছে” flip-flop, যা সবুজ দেওয়া পর্যন্ত অনুরোধ ধরে রাখে (অনুশীলন ১)।
  • A4 — বাতি চলে Moore output থেকে, অর্থাৎ শুধু state থেকে; input-এর ঝাঁকুনি সরাসরি বাতিতে যায় না।
  • A5 — বিদ্যুৎ চালু হলে reset. State register কোন মান নিয়ে জেগে উঠবে, তা জানা নেই; তাই power-on reset দিয়ে MG-তে শুরু।

সাধারণ ভুল: সেন্সর বা বোতামের তার সরাসরি FSM-এর next-state logic-এ জুড়ে দেওয়া। Simulation-এ সব ঠিক দেখায়, কারণ testbench input বদলায় clock-এর সুবিধামতো সময়ে; কিন্তু বাস্তব বোর্ডে মাঝে মাঝে state register-এর একটি বিট নতুন মান নেয়, অন্যটি পুরোনো — FSM অপ্রত্যাশিত state-এ চলে যায়।

৩ · প্রজেক্ট A, ধাপ ২ — state diagram, state table আর state assignment

বাতির সম্ভাব্য বৈধ অবস্থা চারটি: প্রধান সড়ক সবুজ (MG — main green), প্রধান সড়ক হলুদ (MY), পার্শ্ব রাস্তা সবুজ (SG), পার্শ্ব রাস্তা হলুদ (SY). প্রতিটি অবস্থায় অন্য রাস্তা লাল। তিনটি input: সেন্সর C, আর timer থেকে দুটি সংকেত — TS (short timeout: হলুদের ৩ tick শেষ) আর TL (long timeout: ৮ tick শেষ)। Output যেহেতু শুধু state-এর ওপর নির্ভর করে, এটি একটি Moore machine; প্রতিটি বৃত্তের ভেতরেই বাতির অবস্থা লেখা।

Traffic light controller-এর Moore state diagram: MG থেকে MY, MY থেকে SG, SG থেকে SY, SY থেকে MG MG · 00 মূল G, পার্শ্ব R MY · 01 মূল Y, পার্শ্ব R SG · 11 মূল R, পার্শ্ব G SY · 10 মূল R, পার্শ্ব Y C·TL TS C′ + TL TS (C·TL)′ TS′ C·TL′ TS′
Moore state diagram. প্রতিটি বৃত্তে state-এর নাম, তার binary code (Q1Q0) আর দুই রাস্তার বাতি। তীরের গায়ে সেই শর্ত, যা সত্য হলে পরের tick-এ ঐ দিকে যাওয়া হয়; বৃত্তের পাশের ফাঁস (self-loop) মানে একই state-এ থাকা।

Diagram আঁকার পরে দুটি সম্পূর্ণতার যাচাই করুন (পর্ব ১৭-এর নিয়ম)। (১) প্রতিটি state থেকে বেরোনো শর্তগুলোর OR = 1: MG-তে C·TL আর (C·TL)′ মিলে সবকিছু। (২) শর্তগুলো পরস্পর বর্জনশীল — একসাথে দুটি তীর সত্য হতে পারে না। SG-তে বেরোনোর শর্ত C′ + TL, থাকার শর্ত তার উল্টো (C′ + TL)′ = C·TL′ (De Morgan). চারটি state-এই দুই শর্ত পূরণ হয়েছে। এবার একই তথ্য state table-এ:

বর্তমান stateশর্তপরের stateপ্রধান সড়কপার্শ্ব রাস্তা
MG (00)C·TLMY (01)GR
(C·TL)′MG (00)
MY (01)TSSG (11)YR
TS′MY (01)
SG (11)C′ + TLSY (10)RG
C·TL′SG (11)
SY (10)TSMG (00)RY
TS′SY (10)

State minimisation: কোনো দুটি state কি সমতুল্য? Moore machine-এ সমতুল্য হতে হলে আগে output এক হতে হবে। এখানে চারটি state-এর বাতির নকশা চারটিই আলাদা, তাই কোনো জোড়া মেলানো যায় না — চার state-ই ন্যূনতম (state minimisation-এর পদ্ধতি পর্ব ১৭-এ)।

State assignment — কোন state-কে কোন বিট

চারটি state-এর জন্য binary-তে ২টি flip-flop যথেষ্ট। কিন্তু কোন state 00, কোনটি 01 — এই state assignment-এর সিদ্ধান্তে logic-এর আকার বদলায়। তিনটি বিকল্প পাশাপাশি:

Stateসাধারণ binaryGray ক্রম (বেছে নেওয়া)One-hot
MG00000001
MY01010010
SG10110100
SY11101000
Flip-flop২২৪
প্রতিটি transition-এ কয়টি বিট বদলায়১ বা ২সবসময় ১সবসময় ২

আমাদের FSM একটি চক্র: MG → MY → SG → SY → MG. Gray ক্রমে (00 → 01 → 11 → 10 → 00) প্রতিটি পদক্ষেপে ঠিক একটি বিট বদলায় — ঠিক যেভাবে Gray code গোনে। লাভ দুটি: পাশাপাশি state K-map-এও পাশাপাশি পড়ে বলে সমীকরণ ছোট হয় (অংশ ৫), আর দুটি বিট একসাথে না বদলানোয় state থেকে বাতি decode করার সময় ক্ষণিক ভুল মান (যেমন 01 থেকে 10 যেতে এক ঝলক 00 বা 11) আসে না — বাতির driver-এ glitch-এর একটি উৎস দূর হয়। বাড়তি উপহার: প্রধান সড়কের লাল ঠিক Q1, আর পার্শ্ব রাস্তার লাল ঠিক Q1′ — কোনো gate-ই লাগে না।

One-hot-এ প্রতিটি state-এর নিজস্ব flip-flop; সমীকরণ diagram থেকে সরাসরি লেখা যায় (“MY-তে যাব যদি MG-তে থাকি আর C·TL, অথবা MY-তে থাকি আর TS′”)। দুটির আসল খরচের তুলনা অংশ ৯-এর প্রোগ্রামে।

৪ · প্রজেক্ট A, ধাপ ৩ — আগে আচরণের মডেল, তারপর gate

পেশাদার নকশাকাররা সমীকরণ বের করার আগে প্রায়ই একটি সরল software মডেল লেখেন, যা state table হুবহু অনুসরণ করে। একে বলা হয় golden model বা reference model — পরের প্রতিটি ধাপ (সমীকরণ, Verilog) এর সাথে মেলানো হবে। মডেলে timer-এর আচরণও স্পষ্ট করতে হয়।

Timer-এর নিয়ম, এক-বেশি-এক-কম ভুল ছাড়া

Timer t প্রতিটি tick-এ ১ বাড়ে, আর যে tick-এ state বদলায় তার পরে ০ থেকে শুরু হয়। কোনো state-এ ঢোকার প্রথম tick-এ t = 0, দ্বিতীয় tick-এ t = 1, …। কোনো state-এ ঠিক k tick থাকতে হলে বেরোনোর সিদ্ধান্ত নিতে হবে সেই tick-এ, যখন t = k − 1. তাই

TS = (t ≥ ৩ − ১) = (t ≥ 2),    TL = (t ≥ ৮ − ১) = (t ≥ 7)

“≥” ব্যবহার করা হয়েছে “=” নয়, কারণ প্রধান সড়কের সবুজে গাড়ি না এলে t বাড়তেই থাকে; তখন TL সত্যই থাকা উচিত, যাতে পরে গাড়ি এলেই সঙ্গে সঙ্গে সাড়া মেলে। ৪-বিটের timer ১৫-তে পৌঁছে থেমে যায় (saturate) — উল্টে ০-তে ফিরে গেলে TL হঠাৎ মিথ্যা হয়ে যেত।

নিচের stepper-এ একটি পূর্ণ চক্র দেখুন: tick ৩-এ পার্শ্ব রাস্তায় একটি গাড়ির সারি আসে, আর tick ১৫-তে সারি ফুরিয়ে যায়। প্রতিটি ধাপে দেখানো মানগুলো সেই tick-এর শুরুর অবস্থা; “পরের state” হলো tick শেষে clock-এর ধারে যা হবে।

FSM আর timer (GMIN = 8, YEL = 3)
Tick
State (Q1Q0)
Timer t
C · TS · TL
প্রধান / পার্শ্ব
পরের state
  1. Tick ০, reset-এর পরে। State MG, timer 0. পার্শ্ব রাস্তায় কেউ নেই (C = 0), তাই C·TL = 0 — MG-তেই থাকা। প্রধান সড়ক সবুজ, পার্শ্ব রাস্তা লাল।
  2. Tick ৩: গাড়ি এল। C = 1, কিন্তু t = 3, তাই TL = 0 (t ≥ 7 নয়)। C·TL = 1·0 = 0 — প্রধান সড়কের ন্যূনতম সবুজ (R3) এখনো পূর্ণ হয়নি, গাড়িটিকে অপেক্ষা করতে হবে। TS = 1 হলেও MG-তে TS কেউ দেখে না।
  3. Tick ৭: ন্যূনতম সময় শেষ। t = 7, তাই TL = 1; C = 1. C·TL = 1, পরের state MY. State বদলাবে বলে ST = 1 — timer পরের tick-এ ০ থেকে শুরু করবে। প্রধান সড়ক সবুজ ছিল tick ০ থেকে ৭ — ঠিক ৮ tick.
  4. Tick ৮: হলুদ শুরু। State MY (01) — Gray ক্রমে শুধু Q0 বদলেছে। Timer 0, TS = 0, তাই থাকা। প্রধান সড়ক হলুদ, পার্শ্ব রাস্তা এখনো লাল — R2-এর “সবুজ → হলুদ” পূর্ণ।
  5. Tick ১০: হলুদ শেষ। t = 2, TS = 1, পরের state SG. হলুদ ছিল tick ৮, ৯, ১০ — ঠিক ৩ tick. এখানে C-এর মান কোনো ভূমিকা রাখে না; হলুদ একবার শুরু হলে পূর্ণ হবেই।
  6. Tick ১১: পার্শ্ব রাস্তা সবুজ। State SG (11), timer 0. C = 1 আর TL = 0, তাই থাকার শর্ত C·TL′ = 1. প্রধান সড়ক এখন লাল — দুই দিকে কখনো একসাথে সবুজ হয়নি (R1).
  7. Tick ১৫: সারি ফুরাল। C = 0, তাই বেরোনোর শর্ত C′ + TL = 1 + 0 = 1 → পরের state SY. পার্শ্ব রাস্তা সবুজ পেল tick ১১ থেকে ১৫ — ৫ tick, সর্বোচ্চ ৮-এর কম, কারণ গাড়ি আগেই শেষ (R5).
  8. Tick ১৬: পার্শ্ব রাস্তা হলুদ। State SY (10), timer 0 — আবার শুধু একটি বিট (Q0) বদলাল। TS = 0, থাকা।
  9. Tick ১৮: পার্শ্ব রাস্তার হলুদ শেষ। t = 2 → TS = 1 → পরের state MG. হলুদ ছিল ১৬, ১৭, ১৮ — আবার ঠিক ৩ tick.
  10. Tick ১৯: চক্র পূর্ণ। আবার MG, timer 0 — নতুন একটি ন্যূনতম সবুজের গোনা শুরু। পুরো চক্রে চারটি state, চারটি বেরোনোর তীর আর চারটি থাকার ফাঁস — প্রতিটি অন্তত একবার ব্যবহার হলো।

একই নিয়ম এবার একটি প্রোগ্রামে, ৬০ tick ধরে। গাড়ির সেন্সরের সংকেত আগে থেকে লেখা (scripted): tick ৩–১৪-এ একটি ছোট সারি, তারপর লম্বা বিরতি, তারপর tick ৩৩ থেকে একটানা ভিড়। প্রোগ্রামটি প্রতিটি tick-এর state, timer আর দুই বাতি একটি সময়রেখায় ছাপায়, আর শেষে R1–R3 পরীক্ষা করে:

// Traffic-light controller = Moore FSM (controller) + 4-bit timer (datapath). 1 tick = 1 s.
const MG = 0, MY = 1, SG = 3, SY = 2;          // state code Q1Q0, Gray order 00-01-11-10
const GMIN = 8, YEL = 3;                        // main green >= 8 ticks, yellow = 3 ticks
function next(s, C, TS, TL) {                   // the state table
  if (s === MG) return C && TL ? MY : MG;       // leave only if a car waits AND min time over
  if (s === MY) return TS ? SG : MY;
  if (s === SG) return !C || TL ? SY : SG;      // side road empty, or max time over
  return TS ? MG : SY;                          // SY
}
const light = s => [s === MG ? "G" : s === MY ? "Y" : "R",   // main road
                    s === SG ? "G" : s === SY ? "Y" : "R"];  // side road
const car = "000111111111111000000000000000000111111111111111111111111111"; // sensor C
let s = MG, t = 0, bad = [];
const row = { tick: "", car: "", state: "", timer: "", main: "", side: "" };
for (let k = 0; k < car.length; k++) {
  const C = car[k] === "1", TS = t >= YEL - 1, TL = t >= GMIN - 1;   // timer flags
  const [m, d] = light(s);
  row.tick += k % 10; row.car += car[k]; row.state += s;
  row.timer += t.toString(16); row.main += m; row.side += d;
  if (m === "G" && d === "G") bad.push(`tick ${k}: both green`);
  const n = next(s, C, TS, TL);
  t = n !== s ? 0 : Math.min(t + 1, 15);        // ST = (next != present) restarts timer
  s = n;
}
for (const w of ["main", "side"]) {             // check the light sequences
  for (const run of row[w].match(/G+|Y+|R+/g).slice(0, -1))
    if (run[0] === "Y" && run.length !== YEL) bad.push(`${w}: yellow lasted ${run.length}`);
  if (/GR|YG|RY/.test(row[w])) bad.push(`${w}: illegal order G->R, Y->G or R->Y`);
}
for (const g of row.main.match(/G+(?=Y)/g) || [])   // every green that ended
  if (g.length < GMIN) bad.push(`main green only ${g.length} ticks`);
for (let a = 0; a < car.length; a += 30)
  for (const key in row) console.log(key.padEnd(6) + row[key].slice(a, a + 30));
console.log("states: 0=MG 1=MY 3=SG 2=SY | timer in hex");
console.log(bad.length ? bad.join("\n") : "all safety checks passed");

আউটপুট (নির্ধারিত — প্রতিবার একই):

tick  012345678901234567890123456789
car   000111111111111000000000000000
state 000000001113333322200000000000
timer 01234567012012340120123456789a
main  GGGGGGGGYYYRRRRRRRRGGGGGGGGGGG
side  RRRRRRRRRRRGGGGGYYYRRRRRRRRRRR
tick  012345678901234567890123456789
car   000111111111111111111111111111
state 000011133333333222000000001113
timer bcde01201234567012012345670120
main  GGGGYYYRRRRRRRRRRRGGGGGGGGYYYR
side  RRRRRRRGGGGGGGGYYYRRRRRRRRRRRG
states: 0=MG 1=MY 3=SG 2=SY | timer in hex
all safety checks passed

প্রথম ৩০ tick stepper-এর সাথে হুবহু মেলে: state সারিতে 00000000 (৮টি MG), 111 (৩টি MY), 33333 (৫টি SG), 222 (৩টি SY), আর প্রতিটি state-বদলের পরে timer 0 থেকে শুরু। Tick ১৯–৩২-এ কোনো গাড়ি নেই, তাই প্রধান সড়ক টানা সবুজ আর timer e (১৪) পর্যন্ত ওঠে; tick ৩৩-এ গাড়ি আসতেই পরের tick-এ হলুদ, কারণ ন্যূনতম সময় অনেক আগেই পূর্ণ। ভিড় একটানা থাকলেও tick ৩৭-এ শুরু হওয়া পার্শ্ব সবুজ ঠিক ৮ tick পরে থামে (33333333) — R5-এর সর্বোচ্চ সীমা। all safety checks passed মানে কোনো tick-এ দুই দিকে সবুজ হয়নি, প্রতিটি হলুদ ঠিক ৩ tick, কোথাও সবুজ থেকে সরাসরি লাল হয়নি, আর প্রধান সড়কের প্রতিটি শেষ হওয়া সবুজ অন্তত ৮ tick. পরীক্ষাটি সত্যিই ভুল ধরে কি না দেখতে MY-এর লাইনে TS ? SG : MY-এর জায়গায় শুধু SG লিখে চালান — প্রোগ্রাম “yellow lasted 1” বলে অভিযোগ করে।

৫ · প্রজেক্ট A, ধাপ ৪ — timer datapath, K-map থেকে সমীকরণ আর যাচাই

Datapath: timer, তুলনা আর ST

Timer একটি ৪-বিটের counter (পর্ব ১৬), যার দুটি নিয়ন্ত্রণ: clear (ST = 1 হলে পরের tick-এ ০) আর enable (শুধু tick-এ গোনে)। Controller আর timer-এর মধ্যে কথা হয় মাত্র তিনটি সংকেতে: controller থেকে ST, timer থেকে TS আর TL. দুটি তুলনা (comparator) সরাসরি timer-এর বিট t3t2t1t0 থেকে:

  • TS = (t ≥ 2) = t3 + t2 + t1 — t ≥ 2 মানে t ০ বা ১ নয়; ০ আর ১-এ ওপরের তিন বিটই 0, বাকি সব মানে অন্তত একটি 1.
  • TL = (t ≥ 7) = t3 + t2·t1·t0 — হয় t ≥ 8 (t3 = 1), নয়তো ঠিক 7 = 0111.
  • ST = (D1 ⊕ Q1) + (D0 ⊕ Q0) — পরের state বর্তমান state থেকে আলাদা হলেই 1. এটি controller-এর next-state logic-এর output থেকে তৈরি, তাই controller আর datapath একটি বন্ধ চক্রে বাঁধা — timing হিসাবে এটি গুরুত্বপূর্ণ (অংশ ৯)।
              ┌──────────────── controller (Moore FSM) ─────────────────┐
 car ─[2-FF sync]─▶ C ─▶┐                                                  │
                        ├─▶ next-state ─D1,D0─▶ [state reg Q1Q0] ─▶ output ─┼─▶ ৬টি বাতি
           TS, TL ─────▶┘     logic                    │          logic    │
              ▲                  └──── ST = (D ≠ Q) ◀──┘                    │
              │                         │                                   │
              │  ┌──────────── datapath (timer) ──▼──────────────┐          │
              └──┤ comparators ◀── [4-bit timer] ◀── +1 / clear   │          │
                 └────────────────────────────────────────────────┘          │
 clk (50 MHz) ─▶ সব flip-flop      tick (প্রতি সেকেন্ডে ১ cycle) ─▶ দুই register-এর enable

D1 — তিন চলকের K-map

State table থেকে Q1-এর পরের মান D1 কখন 1? MY থেকে SG-তে গেলে (01 আর TS), SG-তে থাকলে বা SY-তে গেলে (11 থেকে দুটিরই Q1 = 1), আর SY-তে থাকলে (10 আর TS′)। দেখা যায় D1 শুধু Q1, Q0 আর TS-এর ওপর নির্ভর করে — C আর TL কোনো ভূমিকা রাখে না:

Q1Q0 ↓ / TS →01
00 (MG)00
01 (MY)01
11 (SG)11
10 (SY)10

দুটি group: TS = 1 কলামে সারি 01 আর 11 → Q0·TS; TS = 0 কলামে সারি 11 আর 10 → Q1·TS′. তাই D1 = Q0·TS + Q1·TS′. সারি 11-এর দুটি ঘর মিলিয়ে Q1Q0 নামে তৃতীয় একটি group-ও আঁকা যায়, কিন্তু সেটি বাড়তি — consensus theorem (পর্ব ০৭) অনুযায়ী Q0·TS + Q1·TS′ + Q1Q0 = Q0·TS + Q1·TS′. আর সমীকরণটি ভালো করে দেখুন: TS = 1 হলে D1 = Q0, TS = 0 হলে D1 = Q1 — এটি ঠিক একটি 2:1 mux, যার select TS (পর্ব ১২)।

D0 — চার চলকের K-map, ধাপে ধাপে

D0 নির্ভর করে Q1, Q0, C আর TL-এর ওপর (TS লাগে না — MY থেকে থাকা বা যাওয়া, দুই ক্ষেত্রেই Q0 = 1; SY থেকে, দুই ক্ষেত্রেই 0). K-map-এর সারি Q1Q0 আর কলাম C·TL, দুটিই Gray ক্রমে 00, 01, 11, 10 (পর্ব ০৯):

K-map: সারি Q1Q0, কলাম C TL = 00 · 01 · 11 · 10
00 (MG)
01 (MY)
11 (SG)
10 (SY)
এ পর্যন্ত D0
  1. খালি map. ষোলোটি ঘর — প্রতিটি (state, C, TL) জোড়ার জন্য একটি। প্রতিটি ঘরে লিখতে হবে পরের state-এর Q0 বিট, অর্থাৎ state table-এর “পরের state” কলামের ডান বিট।
  2. Table থেকে ঘর ভরা। MG সারি: শুধু C·TL = 11 হলে MY (01) → Q0 = 1, বাকি তিনটি MG (00) → 0. MY সারি: যাই হোক MY বা SG, দুটিরই Q0 = 1 → চারটিই 1. SG সারি: থাকার শর্ত C·TL′ মানে কলাম 10 → SG (11) → 1; বাকিগুলো SY (10) → 0. SY সারি: MG বা SY — দুটিরই Q0 = 0. মোট minterm (Q1Q0C·TL ক্রমে) ৩, ৪, ৫, ৬, ৭, ১৪।
  3. Group a — পুরো MY সারি। চারটি পাশাপাশি 1, তাই একটি ৪-ঘরের group: সারিতে Q1 = 0, Q0 = 1 স্থির, C আর TL বদলায় → Q1′Q0. সবচেয়ে বড় group আগে।
  4. Group b — MG সারির একা 1. ঘর m3 (00, কলাম 11)-এর একমাত্র 1-প্রতিবেশী তার নিচের m7 (MY সারি, একই কলাম)। দুই ঘরের group: Q1 = 0, C = 1, TL = 1 স্থির, Q0 বদলায় → Q1′·C·TL. m3-কে ঢাকার আর কোনো উপায় নেই, তাই এটি essential.
  5. Group c — SG সারির একা 1. ঘর m14 (11, কলাম 10)-এর 1-প্রতিবেশী শুধু ওপরের m6 (MY, কলাম 10). Group: Q0 = 1, C = 1, TL = 0 → Q0·C·TL′. এটিও essential. সব 1 ঢাকা হয়ে গেছে।
  6. ফল আর যাচাই। D0 = Q1′Q0 + Q1′·C·TL + Q0·C·TL′ — তিনটি term, ৮টি literal. একটি সারি মিলিয়ে দেখুন: SG-তে গাড়ি আছে, সময় বাকি (Q1Q0 = 11, C = 1, TL = 0) → 0 + 0 + 1·1·1 = 1, তাই পরের state-এর Q0 = 1, অর্থাৎ SG-তে থাকা — table-এর সাথে মেলে। বাকি সব সারি নিচের প্রোগ্রাম একসাথে মেলায়।

Output সমীকরণ সরাসরি state code থেকে: প্রধান G = Q1′Q0′, প্রধান Y = Q1′Q0, প্রধান R = Q1; পার্শ্ব G = Q1Q0, পার্শ্ব Y = Q1Q0′, পার্শ্ব R = Q1′. এবার হাতের কাজ যন্ত্রে মেলানো যাক। নিচের প্রোগ্রামে একটি ছোট brute-force minimiser আছে — সব সম্ভাব্য implicant খুঁজে prime implicant রাখে, তারপর সবচেয়ে সস্তা cover বাছে (Quine-McCluskey-এর একই লক্ষ্য, পর্ব ১০)। এটি state table থেকে নিজে সমীকরণ বের করে, তারপর হাতে পাওয়া সমীকরণগুলোকে table-এর ৩২টি (state, input) সারিতে মেলায়:

// State table -> minimal SOP (brute-force prime implicants + cheapest cover) -> verify.
const V = ["Q1", "Q0", "C", "TS", "TL"];        // row m = Q1 Q0 C TS TL as 5 bits, Q1 = MSB
const bit = (m, i) => (m >> (4 - i)) & 1;
function next(s, C, TS, TL) {                   // same state table as the simulator
  if (s === 0) return C && TL ? 1 : 0;          // MG=00 -> MY=01
  if (s === 1) return TS ? 3 : 1;               // MY=01 -> SG=11
  if (s === 3) return !C || TL ? 2 : 3;         // SG=11 -> SY=10
  return TS ? 0 : 2;                            // SY=10 -> MG=00
}
const nx = m => next(m >> 3, bit(m, 2), bit(m, 3), bit(m, 4));
function minimise(f) {                          // a cube is [care mask, value]
  const cubes = [], on = [...Array(32).keys()].filter(f);
  for (let c = 0; c < 32; c++) for (let v = c; ; v = (v - 1) & c) {
    if ([...Array(32).keys()].every(m => (m & c) !== v || f(m))) cubes.push([c, v]);
    if (v === 0) break;
  }
  const P = cubes.filter(([c, v]) => !cubes.some(([d, w]) => d !== c && (d & c) === d && (v & d) === w));
  let best = null, lits = c => c.toString(2).split("1").length - 1;  // cost: terms, literals
  for (let s = 1; s < 2 ** P.length; s++) {
    const sel = P.filter((_, i) => (s >> i) & 1);
    if (!on.every(m => sel.some(([c, v]) => (m & c) === v))) continue;
    const cost = 100 * sel.length + sel.reduce((a, [c]) => a + lits(c), 0);
    if (!best || cost < best.cost) best = { cost, sel };
  }
  return best ? best.sel.map(([c, v]) => V.filter((_, i) => bit(c, i))
    .map(n => n + (bit(v, V.indexOf(n)) ? "" : "'")).join("·")).join(" + ") : "0";
}
const st = m => m >> 3, fns = { D1: m => nx(m) >> 1, D0: m => nx(m) & 1,
  mainG: m => st(m) === 0, mainY: m => st(m) === 1, mainR: m => bit(m, 0) === 1,
  sideG: m => st(m) === 3, sideY: m => st(m) === 2, sideR: m => bit(m, 0) === 0 };
for (const k in fns) console.log(`${k.padEnd(5)} = ${minimise(m => +fns[k](m))}`);
const hand = (q1, q0, C, TS, TL) =>             // equations read off the K-maps by hand
  2 * ((q0 & TS) | (q1 & !TS)) + ((!q1 & q0) | (!q1 & C & TL) | (q0 & C & !TL));
let ok = 0; for (let m = 0; m < 32; m++) ok += hand(...V.map((_, i) => bit(m, i))) === nx(m);
console.log(`hand equations vs state table: ${ok}/32 (state, input) rows match`);

আউটপুট (নির্ধারিত):

D1    = Q0·TS + Q1·TS'
D0    = Q0·C·TL' + Q1'·C·TL + Q1'·Q0
mainG = Q1'·Q0'
mainY = Q1'·Q0
mainR = Q1
sideG = Q1·Q0
sideY = Q1·Q0'
sideR = Q1'
hand equations vs state table: 32/32 (state, input) rows match

Minimiser পেয়েছে শুধু state table আর ৫টি চলক, তবু নিজেই বের করেছে যে D1-এ C আর TL নেই, D0-তে TS নেই। তার D1 আর D0 হাতে পাওয়া সমীকরণের সাথে হুবহু এক (শুধু term-এর ক্রম আলাদা), ছয়টি বাতির সমীকরণও। 32/32 মানে ৪টি state × ৮টি input-সমাহারের প্রতিটিতে সমীকরণের পরের state আর table-এর পরের state এক। কোনো একটি literal ভুল লিখলে (যেমন q0 & C & !TL-এর জায়গায় q0 & C) সংখ্যাটি 30/32-এ নেমে যায় — হাতের K-map-এর ভুল ধরার এটাই সবচেয়ে নিশ্চিত পথ।

৬ · প্রজেক্ট A, ধাপ ৫ — Verilog RTL আর testbench

সমীকরণ হাতে থাকলেও বাস্তবে নকশাকার সাধারণত gate নয়, RTL লেখেন — state table-টিই প্রায় হুবহু case দিয়ে — আর synthesis tool নিজে সমীকরণ বানায়। হাতের সমীকরণের কাজ তখন বোঝা, আর tool-এর ফল যাচাই করা। নিচের module-টি synthesizable, আর পর্ব ১৪-এর নিয়ম মেনে লেখা:

// Traffic-light controller: Moore FSM (controller) + 4-bit timer (datapath).
module traffic_ctrl #(parameter GMIN = 8, YEL = 3) (
    input  wire clk,           // fast board clock, e.g. 50 MHz
    input  wire rst,           // synchronous, active-high: go to MG
    input  wire tick,          // 1-cycle pulse once per second (clock ENABLE)
    input  wire car,           // side-road sensor, already synchronised
    output wire main_g, main_y, main_r,
    output wire side_g, side_y, side_r
);
    localparam [1:0] MG = 2'b00, MY = 2'b01, SG = 2'b11, SY = 2'b10;  // Gray order
    reg  [1:0] state, next;
    reg  [3:0] timer;
    wire ts = (timer >= YEL - 1);      // short timeout: yellow is over
    wire tl = (timer >= GMIN - 1);     // long timeout: min / max green is over

    always @(*) begin                   // next-state logic (combinational)
        next = state;                   // default = stay  -> no inferred latch
        case (state)
            MG: if (car && tl)  next = MY;
            MY: if (ts)         next = SG;
            SG: if (!car || tl) next = SY;
            SY: if (ts)         next = MG;
        endcase
    end

    always @(posedge clk) begin         // state register + timer datapath
        if (rst) begin
            state <= MG;
            timer <= 4'd0;
        end else if (tick) begin
            state <= next;
            if (next != state)        timer <= 4'd0;          // ST: restart
            else if (timer != 4'd15)  timer <= timer + 4'd1;  // saturate at 15
        end
    end

    assign main_g = (state == MG);
    assign main_y = (state == MY);
    assign main_r =  state[1];
    assign side_g = (state == SG);
    assign side_y = (state == SY);
    assign side_r = ~state[1];
endmodule
  • দুটি always: একটি combinational (@(*), blocking =), একটি sequential (@(posedge clk), non-blocking <=)। শুরুর next = state-এর জন্য অনিচ্ছাকৃত latch হয় না।
  • Reset synchronous: শুধু clock-এর ধারে কাজ করে (synchronous reset). অনেক নকশায় asynchronous reset-ও দেখা যায় (পর্ব ১৭–১৮)।
  • tick একটি enable, clock নয়: সব flip-flop একই দ্রুত clock-এ চলে, শুধু tick = 1 হলে নতুন মান নেয়।
  • Output assign দিয়ে, শুধু state থেকে — Moore. চারটি code-ই ব্যবহৃত, তাই এখানে default জরুরি নয়; vending machine-এ লাগবে।

Testbench-এর কাঠামো

Testbench শুধু simulation-এ চলে। দ্রুত simulation-এর জন্য এখানে প্রতিটি clock-ই একটি tick (tick = 1). Input বদলানো হয় clock-এর নিচের ধারে (negedge), যাতে DUT ওপরের ধারে স্থির মান দেখে — testbench-এর সবচেয়ে প্রচলিত race এড়ানোর কৌশল। আর নিরাপত্তার শর্তগুলো প্রতিটি cycle-এ নিজে পরীক্ষা হয় (self-checking):

`timescale 1ns/1ps
module tb_traffic;
    reg  clk = 0, rst = 1, tick = 1, car = 0;
    wire mg, my, mr, sg, sy, sr;
    reg  prev_mg = 0, prev_sg = 0;
    integer k, errors = 0;

    traffic_ctrl #(.GMIN(8), .YEL(3)) dut (.clk(clk), .rst(rst), .tick(tick), .car(car),
        .main_g(mg), .main_y(my), .main_r(mr), .side_g(sg), .side_y(sy), .side_r(sr));

    always #10 clk = ~clk;                       // 50 MHz; here every clock is a tick

    initial begin                                // directed test: same script as the JS model
        @(negedge clk) rst = 0;
        for (k = 0; k < 60; k = k + 1) begin
            car = (k >= 3 && k <= 14) || (k >= 33);
            @(negedge clk);
        end
        if (errors == 0) $display("PASS: all safety checks");
        else             $display("FAIL: %0d errors", errors);
        $finish;
    end

    always @(posedge clk) if (!rst) begin        // R1 and R2 checked every cycle
        if ((mg || my) && (sg || sy))  begin errors = errors + 1; $display("%0t both moving", $time); end
        if (mg + my + mr != 1 || sg + sy + sr != 1) begin errors = errors + 1; $display("%0t bad lamp code", $time); end
        if ((prev_mg && mr) || (prev_sg && sr))  begin errors = errors + 1; $display("%0t green to red", $time); end
        prev_mg <= mg;  prev_sg <= sg;
    end
endmodule

এটি একটি কাঠামো, পূর্ণ verification নয়। আরও কয়েকটি directed test লাগবে: গাড়ি কখনো না আসা, ন্যূনতম সময়ের পরে গাড়ি আসা, চক্রের মাঝখানে reset, আর সেন্সরে এক tick-এর ঝলক। Testbench-এ একই সময়রেখা ছাপিয়ে golden model-এর সাথে মেলানো সবচেয়ে সহজ যাচাই (পুরো তালিকা অংশ ৯-এ)।

৭ · প্রজেক্ট B — vending machine: Mealy না Moore, state table আর minimisation

এবার একটি অফিসের করিডোরে রাখা চায়ের যন্ত্র। দাম ১৫ টাকা। যন্ত্রের coin acceptor (টাকা চিনে নেওয়ার অংশ) প্রতিটি বৈধ টাকা ঢুকলে controller-কে এক cycle-এর একটি পালস পাঠায়: N (৫ টাকা) অথবা D (১০ টাকা)। বাংলাদেশে ১০ টাকা সাধারণত নোট, তাই ধরে নিন acceptor ৫ টাকার কয়েন আর ১০ টাকার নোট দুটিই নেয় — FSM-এর কাছে দুটিই কেবল একটি পালস। এছাড়া একটি “বাতিল” বোতাম X.

নম্বরRequirement
V1জমা ১৫ টাকা হলেই এক কাপ চা (VEND পালস)
V2জমা ২০ টাকা হলে চা আর একটি ৫ টাকা ভাংতি (CHG5 পালস)
V3বাতিল চাপলে জমা টাকা ফেরত (RET পালস — escrow, অর্থাৎ আলাদা খোপে ধরে রাখা টাকা, ফিরিয়ে দেয়)
V4টাকার হিসাব সবসময় মেলে: ঢোকা = বিক্রি × ১৫ + ভাংতি + ফেরত + এখনো জমা
V5Reset-এর পরে জমা ০

ধরে নেওয়া শর্ত: acceptor একবারে একটি টাকাই পাঠায় (N, D, X-এর মধ্যে এক cycle-এ বড়জোর একটি 1); তার পালস আগেই synchronised; আর চা ঢালার যন্ত্র (dispenser) VEND-এর এক cycle-এর পালস পেলেই নিজে পুরো কাজ শেষ করে।

Mealy না Moore?

State হবে “এখন পর্যন্ত কত টাকা জমা”। Mealy machine-এ output থাকে তীরের গায়ে — “১০ টাকা জমা অবস্থায় আরেকটি ১০ এলে: VEND আর CHG5, তারপর ০-তে”। তাই ১৫ বা ২০ টাকার আলাদা state লাগে না। Moore-এ output শুধু state থেকে, তাই “বিক্রি করছি” আর “বিক্রি করছি + ভাংতি দিচ্ছি” — দুটি আলাদা state লাগে:

বিষয়MealyMoore
StateS0, S5, S10 — ৩টিS0, S5, S10, S15 (VEND), S20 (VEND + CHG5) — ৫টি
Flip-flop (binary)২৩
পণ্য কখনশেষ টাকা ঢোকার cycle-এইএক cycle পরে
Output-এর ঝুঁকিinput থেকে output সরাসরি combinational পথ; input-এ glitch থাকলে output-এওoutput শুধু flip-flop থেকে — পরিষ্কার

আমাদের সিদ্ধান্ত Mealy: input-গুলো synchronised, এক cycle-এর পরিষ্কার পালস, তাই Mealy-র প্রধান দুর্বলতা (input-এর ঝাঁকুনি output-এ যাওয়া) এখানে প্রায় নেই; ৩টি state মানে ২টি flip-flop আর ছোট logic; আর VEND আসে টাকার পালসের একই cycle-এ। Output chip-এর বাইরে মোটর চালাতে গেলে অনেকে Mealy output-এর পরে একটি flip-flop বসান — তখন আচরণ কার্যত Moore-এর মতো, এক cycle দেরিতে।

বর্তমানকিছু নাN (৫ টাকা)D (১০ টাকা)X (বাতিল)
S0 (00)S0 / –S5 / –S10 / –S0 / –
S5 (01)S5 / –S10 / –S0 / VENDS0 / RET
S10 (10)S10 / –S0 / VENDS0 / VEND, CHG5S0 / RET

প্রতিটি ঘরে “পরের state / output” — Mealy table এভাবেই লেখা হয়। Code 11 কোনো state-কে দেওয়া হয়নি; এটি একটি unused state, যার কথা পরের অংশে।

State minimisation — একটি বাড়তি state ধরা

প্রথমবার নকশা করতে গিয়ে অনেকেই “৫ + ৫ = ১০” আর “সরাসরি ১০” আলাদা রাখেন: S10a আর S10b. এরা কি আলাদা? Partition পদ্ধতিতে (পর্ব ১৭) দেখা যাক। প্রথমে output দিয়ে ভাগ: প্রতিটি input-এ (কিছু না, N, D, X) output — S0: সব শূন্য; S5: D-তে VEND, X-এ RET; S10a আর S10b দুটিরই: N-এ VEND, D-তে VEND + CHG5, X-এ RET. তাই প্রথম ভাগ {S0}, {S5}, {S10a, S10b}. পরের state দিয়ে: দুটিই কিছু না পেলে নিজের দলে থাকে, আর N, D, X-এ S0-তে যায়। ভাগ আর ভাঙে না, তাই S10a ≡ S10b — একটি S10-এ মিশিয়ে দেওয়া হলো। শিক্ষাটি সরল — state মনে রাখে ভবিষ্যতের জন্য যা দরকার (কত টাকা জমা), ইতিহাস নয় (কোন কোন কয়েন)।

নিচের stepper-এ ১০ + ১০ ক্রমটি দেখুন — Mealy output কীভাবে clock-এর ধারের আগেই আসে:

Controller-এর অবস্থা (দাম ১৫ টাকা)
সময়
State (Q1Q0)
Input N D X
VEND CHG5 RET
পরের state
টাকা: ঢুকল / জমা / বিক্রি / ভাংতি
  1. অপেক্ষা। State S0, কোনো input নেই, সব output 0. জমা ০ টাকা।
  2. প্রথম ১০ টাকা ঢুকল। এই cycle-এ D = 1. Mealy table-এ S0 আর D-এর ঘর: “S10 / –” — কোনো output নয়, শুধু পরের state S10. টাকা এখন escrow-তে, কিন্তু state register এখনো পুরোনো S0 দেখাচ্ছে, কারণ clock-এর ধার আসেনি।
  3. Clock-এর ধার। State register নতুন মান নেয়: S10 (10). D-এর পালস শেষ। জমা ১০ টাকা। এটাই flip-flop-এর কাজ — পালস চলে গেলেও “১০ টাকা জমা” কথাটি মনে থাকে।
  4. দ্বিতীয় ১০ টাকা। State S10, D = 1. Table-এর ঘর: “S0 / VEND, CHG5”. দেখুন, VEND আর CHG5 এই cycle-এই 1 — clock-এর ধারের অপেক্ষা নেই। এটাই Mealy: output = f(state, input). সমীকরণে VEND = Q1·D + … = 1, CHG5 = Q1·D = 1.
  5. ধার: আবার S0. State S0, input-এর পালস শেষ, তাই output-ও 0. Dispenser এক cycle-এর VEND পালস পেয়ে চা ঢালছে, ভাংতির খোপ একটি ৫ টাকা ছেড়েছে। Moore নকশায় এখানে একটি S20 state লাগত, আর পণ্য আসত আরও এক cycle পরে।
  6. টাকার হিসাব (V4). ঢুকেছে ২০, বিক্রি ১ × ১৫ = ১৫, ভাংতি ৫, ফেরত ০, এখনো জমা ০ → ২০ = ১৫ + ৫ + ০ + ০. এক টাকাও হারায়নি, বাড়তিও যায়নি।

সমীকরণ আর ভাংতির logic

Encoding: S0 = 00, S5 = 01, S10 = 10 — জমা টাকা ÷ ৫-এর binary. চলক Q1, Q0, N, D, X. দুই ধরনের don't care আছে: unused state 11, আর অসম্ভব input-সমাহার (একসাথে দুটি বা তিনটি 1). এগুলো কাজে লাগিয়ে ন্যূনতম SOP (scratch script-এ একই minimiser দিয়ে যাচাই করা):

D1 = Q0·N + Q1·N′D′X′ + Q1′Q0′·D    D0 = Q0·N′D′X′ + Q1′Q0′·N
VEND = Q1·N + (Q1 + Q0)·D    CHG5 = Q1·D    RET = (Q1 + Q0)·X

পড়ে দেখুন, প্রতিটি term একটি বাক্য। D1: “৫ জমা আর ৫ এল (→ S10)”, “১০ জমা আর কিছু এল না (S10-তে থাকা)”, “০ জমা আর ১০ এল (→ S10)”. VEND: “১০ জমা আর ৫ এল”, অথবা “কিছু জমা আছে আর ১০ এল”। ভাংতির logic CHG5 = Q1·D — ভাংতি লাগে কেবল একটি ক্ষেত্রে, ১০ জমা থাকা অবস্থায় আরেকটি ১০ এলে, কারণ ১০ + ১০ = ২০ = ১৫ + ৫. অন্য কোনো পথে মোট টাকা ১৫ পেরোয় না: ৫ + ১০ = ১৫, ১০ + ৫ = ১৫, ৫ + ৫ + ৫ = ১৫. RET-এ (Q1 + Q0) মানে “কিছু জমা আছে”; S0-তে বাতিল চাপলে কিছুই হয় না।

নিচের simulator Mealy table হুবহু অনুসরণ করে কয়েকটি ক্রম চালায়, প্রতিটি পদক্ষেপে output আর মোট বিক্রির টাকা ছাপায়, শেষে টাকার হিসাব (V4) আর transition coverage মেলায়:

// Vending machine as a Mealy FSM: price 15 taka, accepts 5 and 10 taka coins.
// State = money held so far. Outputs depend on the state AND this tick's input.
// Input per tick: "5", "10", "X" (cancel) or "-" (nothing). Outputs: VEND, CHG5, RET.
const T = {   //        input: [next state, VEND, CHG5, RET]
  S0:  { "-": ["S0", 0, 0, 0],  "5": ["S5", 0, 0, 0],  "10": ["S10", 0, 0, 0], X: ["S0", 0, 0, 0] },
  S5:  { "-": ["S5", 0, 0, 0],  "5": ["S10", 0, 0, 0], "10": ["S0", 1, 0, 0],  X: ["S0", 0, 0, 1] },
  S10: { "-": ["S10", 0, 0, 0], "5": ["S0", 1, 0, 0],  "10": ["S0", 1, 1, 0],  X: ["S0", 0, 0, 1] },
};
const held = s => Number(s.slice(1));            // S10 holds 10 taka
const tests = { "5+10": ["5", "-", "10"], "10+10": ["10", "10"], "5+5+5": ["5", "5", "5"],
  "10+5": ["10", "-", "5"], "5,cancel": ["5", "X"], "10,cancel": ["10", "X"],
  "cancel only": ["-", "X"] };
let inserted = 0, kept = 0, change = 0, refund = 0, sold = 0, s = "S0", seen = new Set();
console.log("state  in   next  VEND CHG5 RET   collected");
for (const [name, seq] of Object.entries(tests)) {
  console.log(`-- ${name}`);
  for (const inp of seq) {
    const [n, vend, chg, ret] = T[s][inp]; seen.add(s + inp);
    if (inp === "5" || inp === "10") inserted += Number(inp);
    if (ret) refund += held(s);                  // RET opens the escrow: held coins go back
    if (chg) change += 5;
    if (vend) { sold++; kept += 15; }
    console.log(`${s.padEnd(6)} ${inp.padEnd(4)} ${n.padEnd(5)}  ${vend}    ${chg}    ${ret}     ${kept}`);
    s = n;
  }
}
const balance = inserted === kept + change + refund + held(s);
console.log(`inserted ${inserted} = sold ${sold} x 15 (${kept}) + change ${change}` +
  ` + refunded ${refund} + held ${held(s)}`);
console.log(balance ? "money conserved: every taka is accounted for" : "MONEY LOST!");
console.log(`transition coverage: ${seen.size}/12 (state, input) pairs exercised`);

আউটপুট (নির্ধারিত):

state  in   next  VEND CHG5 RET   collected
-- 5+10
S0     5    S5     0    0    0     0
S5     -    S5     0    0    0     0
S5     10   S0     1    0    0     15
-- 10+10
S0     10   S10    0    0    0     15
S10    10   S0     1    1    0     30
-- 5+5+5
S0     5    S5     0    0    0     30
S5     5    S10    0    0    0     30
S10    5    S0     1    0    0     45
-- 10+5
S0     10   S10    0    0    0     45
S10    -    S10    0    0    0     45
S10    5    S0     1    0    0     60
-- 5,cancel
S0     5    S5     0    0    0     60
S5     X    S0     0    0    1     60
-- 10,cancel
S0     10   S10    0    0    0     60
S10    X    S0     0    0    1     60
-- cancel only
S0     -    S0     0    0    0     60
S0     X    S0     0    0    0     60
inserted 80 = sold 4 x 15 (60) + change 5 + refunded 15 + held 0
money conserved: every taka is accounted for
transition coverage: 12/12 (state, input) pairs exercised

প্রতিটি ক্রম শেষে state আবার S0. 10+10-এর সারি S10 10 S0 1 1 0 — VEND আর CHG5 একসাথে, stepper-এর চতুর্থ ধাপের মতো; 5+5+5-এ তৃতীয় কয়েনে শুধু VEND. দুটি বাতিলে RET = 1, মোট ৫ + ১০ = ১৫ টাকা ফেরত। collected শুধু বিক্রির টাকা: ৪ × ১৫ = ৬০। শেষের সমীকরণ ৮০ = ৬০ + ৫ + ১৫ + ০, তাই money conserved. আর 12/12 মানে table-এর প্রতিটি ঘর (৩ state × ৪ input) অন্তত একবার চালানো হয়েছে — প্রথম খসড়ায় “কিছু না” input না থাকায় সংখ্যাটি কম আসছিল, তখন "-" যোগ করা হয়। Coverage না গুনলে এই ফাঁক চোখেই পড়ত না।

৮ · প্রজেক্ট B — কঠিন edge case আর Verilog

টাকার যন্ত্রে “সাধারণত ঠিক চলে” যথেষ্ট নয়। Design review-তে প্রথম প্রশ্নই হয়: অস্বাভাবিক কিছু ঘটলে কী হবে?

  • একসাথে দুটি টাকা? Acceptor-এর পথে একবারে একটিই ঢোকে; দুটি একসাথে আটকালে acceptor চিনতে না পেরে ফেরতের ট্রেতে ফেলে — FSM কোনো পালসই পায় না। তাই N·D = 1 সমীকরণে don't care. তবু RTL-এ if … else if-এর ক্রমে একটি স্পষ্ট অগ্রাধিকার (X, তারপর N, তারপর D) রাখা হয়েছে, যাতে আচরণ নির্ধারিত থাকে। Don't care বাদ দিয়ে “অবৈধ input মানে কিছু না” ধরলে minimiser-এ D1-এর খরচ প্রায় দ্বিগুণ — নিরাপত্তার একটি দাম আছে।
  • চা ঢালার সময় আরেকটি টাকা? VEND-এর সাথেই state S0, তাই নতুন টাকা পরের কেনায় গোনা হয়; কিন্তু dispenser ব্যস্ত থাকাকালীন দ্বিতীয় VEND হারাতে পারে। সমাধান: dispenser-এর BUSY সংকেত, আর BUSY = 1 হলে acceptor বন্ধ — টাকা যান্ত্রিকভাবেই ফেরতের ট্রেতে যায়।
  • S0-তে বাতিল: কিছু জমা নেই, তাই RET = 0. RET = X লিখলে খালি escrow-র ঢাকনা অকারণে খুলত — ক্ষতি নেই, কিন্তু পরীক্ষায় অবাক করা আচরণ।
  • Unused state 11 — সত্যিকারের বিপদ। বিদ্যুতের ঝাঁকুনিতে state register 11 হয়ে গেলে don't care দিয়ে সরল করা সমীকরণ কী করে? Input না থাকলে D1 = Q1·N′D′X′ = 1, D0 = Q0·N′D′X′ = 1 → 11-তেই আটকে থাকে; তখন একটি ৫ টাকা এলে VEND = Q1·N = 1 — ৫ টাকায় চা! Minimiser don't care-কে নিজের সুবিধামতো 0 বা 1 ধরেছে, আর সেই ধরে নেওয়া এখানে টাকার ক্ষতি। প্রতিকার: power-on reset, আর RTL-এ default: next = S0 (একটি self-correcting FSM). কিছু synthesis tool এই recovery পথও মুছে দিতে পারে, যদি না “safe FSM” ধরনের বিকল্প চালু থাকে — tool-এর manual দেখুন।
// Tea vending controller: price 15, coins 5 and 10, cancel returns the escrow.
module vending (
    input  wire clk, rst,
    input  wire coin5, coin10, cancel,     // 1-cycle synchronised pulses
    output reg  vend, change5, ret          // Mealy outputs (combinational)
);
    localparam [1:0] S0 = 2'd0, S5 = 2'd1, S10 = 2'd2;
    reg [1:0] state, next;

    always @(*) begin
        next = state;  vend = 1'b0;  change5 = 1'b0;  ret = 1'b0;   // defaults
        case (state)
            S0:  if (coin5)       next = S5;
                 else if (coin10) next = S10;
            S5:  if (cancel)      begin ret  = 1'b1;  next = S0;  end
                 else if (coin5)  next = S10;
                 else if (coin10) begin vend = 1'b1;  next = S0;  end
            S10: if (cancel)      begin ret  = 1'b1;  next = S0;  end
                 else if (coin5)  begin vend = 1'b1;  next = S0;  end
                 else if (coin10) begin vend = 1'b1;  change5 = 1'b1;  next = S0;  end
            default:              next = S0;    // unused code 2'b11: recover safely
        endcase
    end

    always @(posedge clk)
        if (rst) state <= S0;
        else     state <= next;
endmodule

Output এখানে reg হলেও flip-flop নয় — combinational always @(*)-এর ভেতরে মান পায় বলে Verilog-এর নিয়মে reg লিখতে হয় (পর্ব ১৪); শুরুর চারটি default-এ latch হয় না। Testbench-এ JS simulator-এর সাতটি ক্রম চালিয়ে প্রতিটি cycle-এ টাকার হিসাব (V4) মেলানো সবচেয়ে কার্যকর self-check, সাথে state 11 থেকে শুরু করা একটি test.

৯ · Design review — যাচাইয়ের পরিকল্পনা, timing, resource আর FPGA ল্যাব

যাচাইয়ের পরিকল্পনা

Testকী চালাবেনকী আশা করবেন
T1 resetযেকোনো state থেকে rstMG / S0 (R6, V5)
T2 গাড়ি নেই৬০ tick C = 0প্রধান সড়ক সবসময় সবুজ (R4)
T3 আগে আসা গাড়িtick ৩-এ C = 1tick ৭ পর্যন্ত অপেক্ষা (R3)
T4 দেরিতে আসা গাড়িদীর্ঘ সবুজের পরে C = 1পরের tick-এই হলুদ
T5 লম্বা সারিC = 1 একটানাপার্শ্ব সবুজ ঠিক ৮ tick (R5)
T6 সেন্সরের ঝলকএক tick-এর C = 1আচরণ নথিভুক্ত করুন (নিচে দেখুন)
T7 টাকার সব পথ৩ state × ৪ input১২/১২ coverage, হিসাব মেলে (V4)
T8 অবৈধ statestate = 11 থেকে শুরুএক cycle-এ S0, কোনো VEND নয়

অংশ ৪-এর ৬০-tick চালনা traffic FSM-এর আটটি তীরের সবকটি ছুঁয়ে যায়, SG থেকে বেরোনোর দুই কারণসহ (গাড়ি ফুরানো আর সর্বোচ্চ সময়) — scratch script-এ গুনে দেখা। T6 একটি নকশার প্রশ্ন সামনে আনে: ন্যূনতম সবুজের পরে সেন্সরে এক tick-এর ভুল ঝলকেও পুরো চক্র শুরু হয়। অনেক বাস্তব নকশায় সেন্সর “পরপর দুই tick 1” হলে তবেই গাড়ি ধরা হয় — review-তেই এমন সিদ্ধান্ত বেরিয়ে আসে।

Timing — নির্বাচিত clock-এ নকশা কি চলবে?

পর্ব ১৮-এর সূত্র: সর্বনিম্ন clock period T ≥ tclk→Q + (সবচেয়ে লম্বা combinational পথ) + tsetup. ধরুন একটি উদাহরণ-প্রযুক্তিতে tclk→Q = ১.০ ns, NOT = ০.৪, AND = ০.৮, OR = ০.৯, XOR = ১.০ ns আর tsetup = ০.৬ ns. Traffic controller-এর সবচেয়ে লম্বা পথ খুঁজি:

  • Timer → TL → D0: clk→Q (১.০) + TL-এর AND আর OR (০.৮ + ০.৯) + TL′-এর NOT (০.৪) + Q0·C·TL′-এর AND (০.৮) + D0-এর OR (০.৯) + setup (০.৬) = ৫.৪ ns.
  • সেই পথ → ST → timer clear: D0 তৈরি হয় ৪.৮ ns-এ; তারপর XOR (১.০) + OR (০.৯) = ST ৬.৭ ns-এ; clear-এর NOT আর AND (০.৪ + ০.৮) = ৭.৯; setup যোগে ৮.৫ ns. এটিই critical path — controller থেকে datapath-এ ফেরার চক্র।

তাই Tmin = ৮.৫ ns, fmax = ১ ÷ ৮.৫ ns ≈ ১১৭.৬ MHz. বোর্ডের ৫০ MHz clock-এর period ২০ ns, তাই slack = ২০ − ৮.৫ = ১১.৫ ns — নিরাপদ। আমরা নকশাটি ১ Hz-এর আলাদা clock-এ না চালিয়ে দ্রুত clock আর tick enable-এ চালাই, যাতে timing-এর হিসাব একটি মাত্র clock-এর ওপর হয়। Hold শর্ত আর asynchronous input-এর synchroniser-সহ পর্ব ১৮-এর পুরো চেকলিস্ট মেলাতে হবে।

Resource গণনা — binary বনাম one-hot

Traffic controller-এ binary encoding-এ flip-flop ২ (state) + ৪ (timer) = ৬টি। কিন্তু শুধু flip-flop দিয়ে খরচ মাপা যায় না — logic কত বড়? নিচের প্রোগ্রাম দুই FSM-এর জন্য দুই encoding-এ পূর্ণ next-state আর output logic-এর ন্যূনতম SOP-এর term আর literal গোনে। Binary-তে unused code আর অসম্ভব input don't care; one-hot-এ প্রতিটি তীর একটি AND term (state-বিট × শর্ত), যেভাবে হাতে লেখা হয়:

function sop(n, f) {   // minimal SOP of f (1, 0, null = don't care) -> {t: terms, l: literals}
  const all = [...Array(1 << n).keys()], on = all.filter(m => f(m) === 1), C = [];
  for (let c = 0; c < 1 << n; c++) for (let v = 0; v < 1 << n; v++)    // every cube (care, value)
    if (!(v & ~c) && all.every(m => (m & c) !== v || f(m) !== 0)) C.push([c, v]);
  const P = C.filter(([c, v]) => !C.some(([d, w]) => d !== c && (d & c) === d && (v & d) === w));
  let best = { t: on.length ? 99 : 0, l: 0 };   // primes P; now the cheapest cover of "on"
  for (let s = 1; on.length && s < 1 << P.length; s++) {
    const sel = P.filter((_, i) => (s >> i) & 1);
    if (sel.length > best.t || !on.every(m => sel.some(([c, v]) => (m & c) === v))) continue;
    const l = sel.reduce((a, [c]) => a + c.toString(2).split("1").length - 1, 0);
    if (sel.length < best.t || l < best.l) best = { t: sel.length, l };
  } return best; }
const traffic = { S: 4, code: [0, 1, 3, 2], valid: () => true, step: (s, i) => {  // i = C TS TL
  const C = i >> 2, TS = (i >> 1) & 1, TL = i & 1;      // outputs: mG mY mR sG sY sR (Moore)
  return [[C && TL ? 1 : 0, TS ? 2 : 1, !C || TL ? 3 : 2, TS ? 0 : 3][s],
          [s === 0, s === 1, s > 1, s === 2, s === 3, s < 2].map(Number)]; } };
const VT = [[[0, 0, 0, 0], [1, 0, 0, 0], [2, 0, 0, 0], [0, 0, 0, 0]],   // inputs -, 5, 10, X
            [[1, 0, 0, 0], [2, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]],   // [next, VEND, CHG5, RET]
            [[2, 0, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1]]];  // (Mealy)
const vending = { S: 3, code: [0, 1, 2], valid: i => (i & (i - 1)) === 0,  // i = N D X, one-hot
  step: (s, i) => { const [n, ...o] = VT[s][[0, 4, 2, 1].indexOf(i)]; return [n, o]; } };
const add = (a, b) => ({ t: a.t + b.t, l: a.l + b.l }), Z = { t: 0, l: 0 };
console.log("FSM      encoding  FFs  next-state terms/literals  output terms/literals");
for (const [name, M] of [["traffic", traffic], ["vending", vending]]) {
  let bn = Z, bo = Z, hn = Z, ho = Z, nO = M.step(0, 0)[1].length;
  const g = (m, pick) => { const s = M.code.indexOf(m >> 3);           // unused code: don't care
    return s < 0 || !M.valid(m & 7) ? null : pick(M.step(s, m & 7)); };
  for (let b = 0; b < 2; b++) bn = add(bn, sop(5, m => g(m, r => (M.code[r[0]] >> b) & 1)));
  for (let k = 0; k < nO; k++) bo = add(bo, sop(5, m => g(m, r => r[1][k])));
  for (let s = 0; s < M.S; s++) for (let j = 0; j < M.S + nO; j++) {   // one-hot: one AND per arc
    const r = i => M.step(s, i), c = sop(3, i => !M.valid(i) ? null : j < M.S ? +(r(i)[0] === j) : r(i)[1][j - M.S]);
    if (j < M.S) hn = add(hn, { t: c.t, l: c.l + c.t }); else ho = add(ho, { t: c.t, l: c.l + c.t }); }
  for (const [e, f, a, b] of [["binary", 2, bn, bo], ["one-hot", M.S, hn, ho]])
    console.log(`${name.padEnd(8)} ${e.padEnd(9)} ${f}    ${(a.t + " / " + a.l).padEnd(27)}${b.t} / ${b.l}`);
}

আউটপুট (নির্ধারিত):

FSM      encoding  FFs  next-state terms/literals  output terms/literals
traffic  binary    2    5 / 12                     6 / 10
traffic  one-hot   4    10 / 22                    8 / 8
vending  binary    2    5 / 16                     6 / 12
vending  one-hot   3    11 / 27                    6 / 12

Traffic-এর binary সারি 5 / 12 হাতের হিসাবের সাথে মেলে (D1-এ ২ term ৪ literal, D0-তে ৩ term ৮ literal), বাতির output 6 / 10. One-hot-এ flip-flop দ্বিগুণ, next-state logic প্রায় দ্বিগুণ (10 / 22), কিন্তু output প্রায় বিনা খরচে (8 / 8 — চারটি বাতি সরাসরি flip-flop থেকে, দুটি লাল দুই flip-flop-এর OR). Vending-এ binary-র don't care (state 11) বড় সুবিধা দেয়: 5 / 16 বনাম 11 / 27. ছোট FSM-এ gate-স্তরে তাই binary সস্তা। FPGA-তে হিসাব আলাদা — প্রতিটি LUT ৪–৬টি input-এর যেকোনো function বানায়, তাই one-hot-এর প্রতিটি সরু সমীকরণ প্রায়ই একটি LUT-এ ধরে যায়, আর flip-flop প্রচুর; এজন্য FPGA tool প্রায়ই one-hot বেছে নেয়। দুই controller-এ কয়েকটি থেকে এক-দেড় ডজন LUT লাগার কথা, তবে সঠিক সংখ্যা synthesis report-ই বলবে।

সম্ভাব্য সম্প্রসারণ

  • ২০ টাকার নোট: S0-তে ২০ এলে ভাংতি ৫, S5-তে ১০, S10-তে ১৫ — তাই CHG5 আর CHG10 দুটি output, বা ভাংতি ছাড়ার জন্য আলাদা একটি ছোট FSM — আরেকটি controller-datapath ভাগ।
  • বাতিল বোতাম: debounce না করলে এক চাপে কয়েকটি X পালস; আমাদের নকশায় দ্বিতীয়টি S0-তে নিরীহ, কিন্তু সব নকশায় নয়।
  • Display: জমা টাকা দেখাতে দুটি 7-segment display (পর্ব ১২) — Moore output (অনুশীলন ৪); traffic-এ পথচারীর জন্য বাকি সেকেন্ডের countdown.
  • পথচারী পারাপার: অনুশীলন ১-এ।

নকশাটি FPGA ল্যাবে

শিক্ষার্থীদের FPGA বোর্ডে সাধারণত একটি crystal oscillator (অনেক বোর্ডে ৫০ বা ১০০ MHz), কয়েকটি বোতাম, switch আর LED থাকে। নকশা নামাতে লাগে:

  1. Pin constraint: কোন port কোন pin-এ, একটি constraint ফাইলে (নিচে Xilinx-ধাঁচের উদাহরণ; pin নাম কাল্পনিক — বোর্ডের manual দেখুন), সাথে clock-এর period, যাতে timing analysis লক্ষ্য জানে।
  2. Tick generator: ৫০ MHz থেকে প্রতি সেকেন্ডে এক cycle-এর enable — ৫ কোটি পর্যন্ত গোনা counter. ২২৫ ≈ ৩.৩৬ কোটি কম, ২২৬ ≈ ৬.৭১ কোটি যথেষ্ট, তাই ২৬ বিট। একে প্রায়ই clock divider বলা হয়, কিন্তু output-কে নতুন clock হিসেবে নয়, enable হিসেবে ব্যবহার করুন।
  3. বোতাম: প্রতিটি আগে synchroniser, তারপর debounce (কয়েক মিলিসেকেন্ড ধরে একই মান পেলে তবে গ্রহণ), তারপর edge detector দিয়ে এক cycle-এর পালস। নইলে একবার “১০ টাকা” চাপলে FSM কয়েকটি D পালস দেখবে — বিনা পয়সায় চা!
  4. LED আর reset: কিছু বোর্ডে LED বা বোতাম active-low; schematic দেখে প্রয়োজনে উল্টে নিন। একটি বোতাম reset-এর জন্য রাখুন।
// 50 MHz clock -> one-cycle 'tick' every second (a clock ENABLE, not a new clock)
reg [25:0] div  = 26'd0;
reg        tick = 1'b0;
always @(posedge clk) begin
    if (div == 26'd49_999_999) begin
        div  <= 26'd0;
        tick <= 1'b1;
    end else begin
        div  <= div + 26'd1;
        tick <= 1'b0;
    end
end

## constraints (Xilinx XDC style) -- replace PIN_x with the pins in your board manual
set_property PACKAGE_PIN PIN_CLK [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -period 20.000 -name sys_clk [get_ports clk]
set_property PACKAGE_PIN PIN_BTN0 [get_ports car_btn]
set_property PACKAGE_PIN PIN_LED0 [get_ports main_g]

আরও গভীরে: বাস্তব traffic controller-এ মূল FSM-এর পাশে প্রায়ই একটি আলাদা, স্বাধীন নিরাপত্তা-circuit থাকে (যুক্তরাষ্ট্রের মানগুলোতে একে conflict monitor বা malfunction management unit বলা হয়), যা সরাসরি বাতির তারের ভোল্টেজ দেখে; দুই বিরোধী দিকে একসাথে সবুজ দেখলে পুরো সিগন্যালকে লাল ঝলকানির নিরাপদ অবস্থায় পাঠায় — আমাদের testbench-এর assertion-টিই hardware-এ। শিল্পে বড় নকশার নিরাপত্তা-শর্ত শুধু simulation-এ নয়, formal verification tool দিয়েও প্রমাণ করা হয় — tool সব সম্ভাব্য input-ধারা গাণিতিকভাবে বিবেচনা করে দেখায় যে “দুই দিকে সবুজ” state-এ পৌঁছানোই অসম্ভব। আর CPU-র control unit (পর্ব ৩২–৩৩) ঠিক এই প্রজেক্টের মতো একটি FSMD: controller instruction দেখে control signal দেয়, datapath (register, ALU, মেমরি) কাজ করে আর flag ফেরত পাঠায়।

পরীক্ষা ও ইন্টারভিউতে এই ধরনের প্রজেক্ট নিয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • Traffic light controller-এর state diagram আঁকুন — Moore কেন? — বাতি শুধু state থেকে আসে, তাই input-এর ঝাঁকুনি বাতিতে যায় না; চারটি state MG, MY, SG, SY, আর timer-এর TS/TL শর্ত।
  • ৪ state-এর FSM-এ binary আর one-hot-এ কতটি flip-flop? — binary ২ (⌈log2 ৪⌉), one-hot ৪; one-hot-এ next-state logic সরল, FPGA-তে প্রায়ই দ্রুত।
  • Vending machine Mealy-তে কেন কম state লাগে? — output তীরে থাকে, তাই “১৫ জমা” বা “২০ জমা” আলাদা state না রেখেই শেষ কয়েনের transition-এ পণ্য দেওয়া যায় — ৩টি বনাম ৫টি।
  • বোতাম সরাসরি FSM-এ দিলে কী সমস্যা? — bounce-এ এক চাপে একাধিক পালস, আর asynchronous বলে metastability; সমাধান synchroniser + debouncer + edge detector.
  • Unused state কেন বিপজ্জনক? — don't care দিয়ে সরল করা logic সেখানে অপ্রত্যাশিত আচরণ (আটকে থাকা বা ভুল output) দিতে পারে; reset আর default-এ নিরাপদ state-এ ফেরানো দরকার।

১০ · AI-এর যুগে — প্রজেক্টে AI-কে সহকারী রাখুন, বিচারক নয়

AI চ্যাটবট কয়েক সেকেন্ডে একটি traffic light controller-এর Verilog দেবে — কিন্তু সেটি আপনার requirement জানে না, আর প্রায়ই চোখে না পড়া ভুল আনে। এমন প্রজেক্টে AI-এর সাধারণ ভুল:

AI প্রায়ই যা ভুল করেকীভাবে ধরবেন
Timer-এ এক-বেশি-এক-কম ভুল (হলুদ ৩-এর জায়গায় ৪ tick), বা timer state বদলালে শূন্য না করাGolden model-এর সময়রেখার সাথে simulation মেলান; হলুদের দৈর্ঘ্য গুনুন
K-map-এ ভুল group বা Gray ক্রম ভুলে সারি সাজানোসমীকরণ state table-এর সব সারিতে বসিয়ে script-এ মেলান
Non-synthesizable Verilog: # delay, initial-এ logic, একই reg দুই always-এ, default ছাড়া combinational alwaysSynthesis চালিয়ে latch/multiple-driver warning দেখুন (পর্ব ১৪)
Mealy আর Moore গুলিয়ে ফেলা — বলে Moore, কিন্তু output-এ input ব্যবহারOutput-এর assign-এ শুধু state আছে কি না দেখুন
Button আর sensor-এর synchroniser/debounce বাদ দেওয়া; ধীর clock বানাতে counter-এর বিটকে clock বানানোপ্রতিটি বাইরের input-এর পথ আঁকুন; সব flip-flop একই clock-এ কি না দেখুন
Unused state আর reset ভুলে যাওয়া; টাকার হিসাব না মেলানোঅবৈধ state থেকে test; প্রতিটি cycle-এ টাকার সমীকরণ assert করুন
বোর্ডের pin নাম, clock কম্পাঙ্ক বা FPGA-র LUT-সংখ্যা বানিয়ে বলাবোর্ডের manual, chip-এর datasheet আর synthesis report

AI-কে নকশার সিদ্ধান্ত না দিয়ে, আপনার নকশা ভাঙার কাজ দিন — পর্যালোচক হিসেবে সে অনেক বেশি কাজের:

আমি একটি traffic light controller বানাচ্ছি: চার state-এর Moore FSM (MG=00, MY=01, SG=11, SY=10), input C (পার্শ্ব রাস্তার synchronised সেন্সর), TS = timer ≥ 2, TL = timer ≥ 7; timer প্রতিটি state-বদলে শূন্য হয়। আমার সমীকরণ: D1 = Q0·TS + Q1·TS′, D0 = Q1′Q0 + Q1′·C·TL + Q0·C·TL′. আমার Verilog নিচে দিলাম। ১. প্রতিটি (state, C, TS, TL) সমাহারের জন্য টেবিল বানিয়ে দেখাও আমার সমীকরণ ঠিক কি না — অনুমান নয়, প্রতিটি সারি হিসাব করে। ২. আমার Verilog-এ কোনো non-synthesizable অংশ, inferred latch বা reset-এর সমস্যা আছে কি না বলো। ৩. দুই দিকে সবুজ বা হলুদ ছাড়া লাল হওয়ার কোনো পথ আছে কি না খোঁজো; থাকলে input-ক্রমটি দাও। ৪. আমার testbench-এ কোন directed test বাকি আছে, তালিকা দাও। নিশ্চিত না হলে বলো, বানিয়ে লিখো না।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

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

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

প্র ০১প্রথম traffic signal থেকে আজকের “adaptive” সিগন্যাল পর্যন্ত — controller-এর ভেতরের FSM কতটা বদলেছে? আর ঢাকার অনেক মোড়ে বাতি থাকা সত্ত্বেও কেন প্রায়ই হাতে নিয়ন্ত্রণ দেখা যায়?

সাধারণভাবে বলা হয়, প্রথম traffic signal বসানো হয়েছিল ১৮৬৮ সালে লন্ডনে, পার্লামেন্ট ভবনের কাছে — রেলের সিগন্যালের নকশা থেকে নেওয়া একটি গ্যাসের বাতি, যা একজন পুলিশ সদস্য হাতে ঘোরাতেন। কয়েক সপ্তাহ পরে গ্যাস ফেটে সেটি বন্ধ হয়ে যায় বলে জানা যায়। বিদ্যুতের লাল-সবুজ সিগন্যাল আসে বিংশ শতকের শুরুতে আমেরিকায় (প্রায়ই ১৯১৪ সালের Cleveland শহরের উদাহরণ দেওয়া হয়), আর তিন রঙের, হলুদসহ সিগন্যালের কৃতিত্ব সাধারণত দেওয়া হয় ১৯২০ সালের দিকে Detroit-এর একজন পুলিশ কর্মকর্তাকে।

প্রথম দিকের স্বয়ংক্রিয় controller ছিল যান্ত্রিক: একটি মোটর ধীরে ধীরে একটি ড্রাম বা cam ঘোরাত, আর ড্রামের প্রতিটি অবস্থান কিছু সুইচ চাপত। ড্রামের প্রতিটি অবস্থানই একটি state — যান্ত্রিক যুগেও এটি একটি FSM ছিল, শুধু input ছাড়া, “fixed time” ধরনের। পরে আসে গাড়ি-সংবেদী (vehicle-actuated) controller — ঠিক এই পর্বের প্রজেক্ট, যেখানে সেন্সর FSM-এর input. তারপর microprocessor-ভিত্তিক controller, আর ১৯৭০-৮০-র দশক থেকে এমন ব্যবস্থা (যেমন অস্ট্রেলিয়ায় তৈরি SCATS বা যুক্তরাজ্যের SCOOT), যেখানে পুরো শহরের অনেক মোড়ের সিগন্যাল সমন্বয় করে গাড়ির প্রবাহ অনুযায়ী সময় বদলানো হয়। তবু প্রতিটি মোড়ের নিরাপত্তার মূল যুক্তি একটি ছোট FSM-ই রয়ে গেছে; বদলেছে মূলত timer-এর মান কে, কীভাবে ঠিক করে।

ঢাকার অনেক মোড়ে সিগন্যাল বাতি থাকলেও ট্রাফিক পুলিশকে হাতের ইশারায় নিয়ন্ত্রণ করতে দেখা যায়, আর স্বয়ংক্রিয় সিগন্যাল চালুর উদ্যোগ বিভিন্ন সময়ে নেওয়া হয়েছে বলে সংবাদমাধ্যমে এসেছে। কারণ হিসেবে প্রায়ই বলা হয় বাস, রিকশা আর পথচারীর মিশ্র চলাচল, লেন না মানা, আর এক মোড়ের জট পাশের মোড়ে ছড়িয়ে পড়ার কথা। প্রকৌশলের ভাষায় বললে, সমস্যাটি প্রায়ই FSM-এ নয়, requirement আর assumption-এ: আমাদের নকশা ধরে নেয় যে চালকেরা লাল বাতিতে থামেন আর সেন্সর গাড়িকে নির্ভুলভাবে চেনে। পরিবেশ এই ধারণা না মানলে সবচেয়ে নিখুঁত circuit-ও কাজে আসে না।

মূল উপলব্ধি: Controller-এর FSM তৈরি করা সহজ অংশ; কঠিন অংশ হলো পরিবেশকে সঠিকভাবে বোঝা আর লিখে রাখা — কোন input কতটা বিশ্বাসযোগ্য, মানুষ কী করবে, আর কোন নিরাপত্তার শর্ত কোনো অবস্থাতেই ভাঙা যাবে না।

প্র ০২ডিজিটাল লজিকের বইগুলোতে traffic light controller এত জনপ্রিয় উদাহরণ কেন? “controller আর datapath আলাদা রাখা” — এই ধারণা একটি মোড় থেকে একটি CPU পর্যন্ত কীভাবে পৌঁছায়?

১৯৮০ সালে Carver Mead আর Lynn Conway-এর লেখা Introduction to VLSI Systems বইটি chip নকশার শিক্ষায় একটি বড় বাঁক হিসেবে পরিচিত — এটি দেখিয়েছিল যে বিশ্ববিদ্যালয়ের শিক্ষার্থীরাও পুরো chip নকশা করতে পারেন। প্রায়ই বলা হয়, সেই বইয়ে একটি মহাসড়ক আর একটি গ্রামের রাস্তার মোড়ের traffic light controller উদাহরণ হিসেবে দেখানো হয়েছিল, যার next-state logic একটি PLA-তে বসানো। এরপর অনেক পাঠ্যবইয়ে একই রকম “highway–farm road” controller প্রচলিত হয় — এই পর্বের নকশাও সেই ঐতিহ্যের। উদাহরণটি টিকে আছে কারণ এতে ঠিক প্রয়োজনীয় সবকিছু আছে, বাড়তি জটিলতা ছাড়া: কয়েকটি state, একটি বাইরের সেন্সর, সময় মাপার জন্য একটি counter, আর একটি স্পষ্ট নিরাপত্তার শর্ত।

এর পেছনের বড় ধারণাটি controller-datapath ভাগ। ১৯৬০-এর দশকে Hewlett-Packard-এ Thomas Osborne-এর কাজ থেকে ASM chart (algorithmic state machine) পদ্ধতি এসেছিল বলে জানা যায়, আর ১৯৭৩ সালে Christopher Clare-এর একটি বই এটি ছড়িয়ে দেয়। ASM chart-এ একটি নকশাকে দুই ভাগে দেখা হয়: একটি FSM, যা প্রতিটি ধাপে ঠিক করে কী করতে হবে, আর register, counter, adder-এর একটি datapath, যা আসল হিসাব করে। পরে এই ধরনের মডেলকে FSMD নামে ডাকা হয়, আর high-level synthesis tool-গুলোও C-ধাঁচের কোড থেকে ঠিক এমন একটি controller আর datapath বানায়।

CPU-ও এই ছাঁচে গড়া। পর্ব ৩২-এ দেখবেন single-cycle datapath — register file, ALU, মেমরি, mux. পর্ব ৩৩-এ control unit, যা instruction-এর opcode দেখে প্রতিটি mux-এর select আর register-এর write enable ঠিক করে, ঠিক যেভাবে আমাদের FSM timer-কে ST দিয়ে নিয়ন্ত্রণ করে। আর datapath থেকে flag (যেমন “ফল শূন্য”) controller-এ ফেরে, যেভাবে timer থেকে TS আর TL ফেরে। পর্ব ৩৪-এর multi-cycle নকশায় control unit সত্যিই একটি বহু-state FSM, আর microprogramming-এ সেই FSM-এর table একটি মেমরিতে লেখা থাকে।

মূল উপলব্ধি: “কখন কী করব” আর “কীভাবে করব” আলাদা রাখা জটিলতা সামলানোর সবচেয়ে কার্যকর কৌশলগুলোর একটি। একটি মোড়ের সিগন্যালে যে ভাগ চারটি state আর একটি counter, একটি CPU-তে সেটিই control unit আর datapath.

প্র ০৩টাকা নেওয়া যন্ত্রের ইতিহাস কত পুরোনো, আর আধুনিক vending machine-এ কয়েন, নোট, কার্ড বা মোবাইল পেমেন্ট — এত রকম উৎস একটি FSM কীভাবে সামলায়?

টাকা দিয়ে চালানো যন্ত্রের সবচেয়ে পুরোনো যে বর্ণনা প্রায়ই উদ্ধৃত হয়, তা আলেকজান্দ্রিয়ার হিরোর (Hero of Alexandria) — প্রথম শতাব্দীর একজন প্রকৌশলী। তাঁর লেখায় একটি মন্দিরের পবিত্র পানির যন্ত্রের কথা আছে: কয়েন ফেললে সেটি একটি ছোট পাল্লায় পড়ে, পাল্লার ভারে একটি লিভার কাত হয়ে একটি ভালভ খোলে, কিছু পানি পড়ে, তারপর কয়েন পিছলে গেলে ভালভ বন্ধ হয়। আধুনিক অর্থে বাণিজ্যিক coin-operated যন্ত্র ছড়ায় উনিশ শতকের শেষ দিকে ইংল্যান্ডে, পোস্টকার্ড বা বই বিক্রির যন্ত্র দিয়ে।

আজকের যন্ত্রে টাকা চেনার কাজটি আলাদা একটি অংশের — coin mechanism বা bill validator. কয়েন চেনা হয় তার আকার, ওজন আর ধাতুর বৈদ্যুতিক-চৌম্বক বৈশিষ্ট্য মেপে; নোট চেনা হয় আলো আর চৌম্বক সেন্সরে। অচেনা টাকা ফেরতের ট্রেতে যায়। চেনা টাকা প্রথমে একটি escrow-তে থাকে, আর বিক্রি নিশ্চিত হলে তবেই ভেতরের বাক্সে যায় — যাতে বাতিল চাপলে ঠিক সেই টাকাই ফেরত দেওয়া যায়। মূল controller আর পেমেন্ট অংশের মধ্যে প্রায়ই একটি শিল্প-মানের protocol চলে (যেমন MDB, Multi-Drop Bus), যাতে ভিন্ন নির্মাতার অংশ জোড়া যায়।

FSM-এর দৃষ্টিতে এই সব জটিলতা একটি সরল বিমূর্ততায় নেমে আসে: “একটি বৈধ ৫ টাকা এল”, “একটি বৈধ ১০ টাকা এল” — একটি করে ঘটনা (event). এই পর্বের অনুমান “acceptor একবারে একটিই পাঠায়” আসলে এই বিমূর্ততারই চুক্তি। বাংলাদেশে bKash বা Nagad-এর মতো মোবাইল ওয়ালেট আর QR কোডে পেমেন্ট এখন অনেক জায়গায় পরিচিত; একটি QR-ভিত্তিক চায়ের যন্ত্র বানালে পেমেন্ট নিশ্চিতকরণ নেটওয়ার্ক থেকে বার্তা হিসেবে আসবে, কিন্তু controller-এর কাছে সেটিও শেষ পর্যন্ত একটি পালস — “১৫ টাকা পাওয়া গেছে”। তখন নতুন edge case আসে: বার্তা দেরিতে এলে বা দুবার এলে কী হবে? সেটি আবার একটি requirement আর assumption-এর প্রশ্ন।

মূল উপলব্ধি: টাকার যন্ত্রের নকশায় সবচেয়ে গুরুত্বপূর্ণ শর্ত হলো “ঢোকা টাকা = বিক্রি + ভাংতি + ফেরত + জমা” — এটিকে প্রতিটি cycle-এ যাচাইযোগ্য একটি invariant হিসেবে লিখে রাখলে প্রায় সব বড় ভুল simulation-এই ধরা পড়ে।

প্র ০৪রেলস্টেশনের সিগন্যাল আর traffic signal — দুই জায়গাতেই ভুল মানে প্রাণহানি। এমন নিরাপত্তা-সংবেদনশীল FSM শুধু simulation-এ পরীক্ষা করা কি যথেষ্ট? শিল্পে আর কী করা হয়?

রেলস্টেশনে একটি ট্রেনকে প্ল্যাটফর্মে ঢোকাতে হলে লাইনের point (দিক বদলানোর অংশ) ঠিক দিকে থাকতে হবে, আর বিরোধী পথের সব সিগন্যাল লাল থাকতে হবে। এই নিয়ম জোর করে মানানোর ব্যবস্থার নাম interlocking. উনিশ শতকে এটি ছিল যান্ত্রিক — লিভারের সারি এমনভাবে জোড়া যে একটি লিভার টানলে বিরোধী লিভার শারীরিকভাবেই আটকে যায়। পরে আসে relay দিয়ে বানানো interlocking, আর এখন computer-ভিত্তিক interlocking. বাংলাদেশ রেলওয়ের বিভিন্ন স্টেশনে আধুনিক interlocking বসানোর কাজের কথাও সংবাদে এসেছে। প্রতিটি প্রজন্মেই মূল চিন্তা এক: কিছু state-সমাহারে পৌঁছানো কোনোভাবেই সম্ভব হবে না।

আমাদের testbench নিরাপত্তা পরীক্ষা করে simulation দিয়ে — অর্থাৎ শুধু সেই input-ধারাগুলোর জন্য, যা আমরা চালিয়েছি। ৬০ tick-এ সব তীর ছুঁয়ে যাওয়া ভালো লক্ষণ, কিন্তু প্রমাণ নয়। এখানে আসে formal verification. Model checking নামের পদ্ধতিতে একটি tool FSM-এর পৌঁছানো যায় এমন সব state গাণিতিকভাবে খুঁজে দেখে, “দুই দিকে সবুজ” state-এ কোনো পথে পৌঁছানো যায় কি না। এই পদ্ধতির জন্য Edmund Clarke, Allen Emerson আর Joseph Sifakis ২০০৭ সালের Turing Award পান। আজ chip শিল্পে assertion লিখে (যেমন SystemVerilog Assertions) সেগুলো simulation আর formal tool — দুই জায়গাতেই ব্যবহার করা প্রচলিত।

দ্বিতীয় স্তর হলো স্বাধীন পর্যবেক্ষক। “আরও গভীরে” অংশে বলা conflict monitor মূল controller-এর logic ব্যবহার করে না; সে সরাসরি বাতির তারে ভোল্টেজ দেখে। ফলে controller-এর নকশায় ভুল, একটি flip-flop-এর হার্ডওয়্যার ত্রুটি বা তারের short — যে কারণেই দুই দিকে সবুজ জ্বলুক, পর্যবেক্ষক ধরে ফেলে আর সিগন্যালকে নিরাপদ অবস্থায় (লাল ঝলকানি) নিয়ে যায়। রেলের interlocking-এ এবং বিমান-মহাকাশ শিল্পেও একই কৌশল: গুরুত্বপূর্ণ কাজের দুটি স্বাধীন বাস্তবায়ন, আর অমিল হলে নিরাপদ অবস্থা।

মূল উপলব্ধি: Simulation বলে “যা চেষ্টা করেছি, তাতে ভুল পাইনি”; formal verification বলে “কোনো input-এ ভুল সম্ভব নয়”; আর স্বাধীন hardware পর্যবেক্ষক বলে “ভুল হলেও ক্ষতি হতে দেব না”। নিরাপত্তা-সংবেদনশীল নকশায় তিনটিই লাগে।

প্র ০৫পুরোনো ল্যাবে ১ Hz পেতে counter-এর শেষ বিটটিকেই পরের circuit-এর clock বানানো হতো। FPGA-তে কেন এটি নিরুৎসাহিত, আর “এক clock, অনেক enable” নিয়মের পেছনের যুক্তি কী?

TTL যুগের ল্যাবে (যেমন 7490 বা 7493 ধরনের counter IC দিয়ে) কম্পাঙ্ক ভাগ করার সহজ উপায় ছিল ripple counter: প্রথম flip-flop মূল clock পায়, আর প্রতিটি পরের flip-flop তার আগেরটির output-কে clock হিসেবে নেয়। প্রতিটি ধাপ কম্পাঙ্ক অর্ধেক করে, আর শেষ বিটটি দিয়ে পরের circuit চালানো হয়। ডিজিটাল ঘড়িতে এটি দারুণ কাজ করে — সেখানে কেবল গোনা দরকার, অন্য কোনো circuit-এর সাথে নিখুঁত তাল মেলানোর প্রয়োজন নেই।

কিন্তু একটি synchronous নকশায় সমস্যা তিনটি। প্রথমত, ripple counter-এর প্রতিটি বিট আগেরটির একটু পরে বদলায়; এই বিলম্ব জমে। শেষ বিট থেকে বানানো clock মূল clock-এর সাথে কয়েক ns পিছিয়ে থাকে (skew), আর দুই clock-এ চলা flip-flop-এর মধ্যে ডেটা গেলে setup বা hold ভেঙে যেতে পারে। দ্বিতীয়ত, FPGA-তে clock-এর জন্য আলাদা, বিশেষ নেটওয়ার্ক আছে, যা chip-এর সব প্রান্তে প্রায় একই সময়ে clock পৌঁছায়। Logic থেকে বানানো clock সাধারণ তারে চলে — সেখানে skew অনেক বেশি, আর tool-কে সতর্কবার্তা দিতে দেখা যায়। তৃতীয়ত, প্রতিটি নতুন clock মানে একটি নতুন clock domain; timing analysis জটিল হয়, আর domain-এর মাঝে সংকেত পাঠাতে synchroniser লাগে (পর্ব ১৮)।

তাই আধুনিক নিয়ম: পুরো নকশা একটি দ্রুত clock-এ, আর ধীর কাজগুলো একটি enable পালস দিয়ে — যেমন আমাদের tick. Flip-flop প্রতিটি clock-এ জাগে, কিন্তু enable = 0 হলে পুরোনো মানই রাখে। সত্যিই ভিন্ন কম্পাঙ্কের clock লাগলে (যেমন একটি দ্রুত মেমরি interface-এর জন্য) FPGA-র ভেতরের PLL বা অনুরূপ clock-ব্যবস্থাপনা block ব্যবহার করা হয়, যা পরিষ্কার, নিয়ন্ত্রিত clock বানায় আর tool তা চেনে। Button debouncing-ও একই দর্শনে: আলাদা ধীর clock নয়, বরং একই clock-এ চলা একটি counter, যা কয়েক মিলিসেকেন্ড ধরে input স্থির থাকলে তবে নতুন মান গ্রহণ করে।

মূল উপলব্ধি: Synchronous নকশার শক্তি হলো সময়ের একটি মাত্র “তাল”। যতবার আপনি নতুন একটি clock বানান, ততবার সেই একক তাল ভাঙে — আর ভাঙা জায়গাতেই সবচেয়ে কঠিন, মাঝে মাঝে দেখা দেওয়া bug লুকিয়ে থাকে।

অনুশীলন

প্রতিটি অনুশীলন একটি প্রজেক্ট-সম্প্রসারণ। আগে acceptance criteria পড়ুন — নকশা তখনই “শেষ”, যখন প্রতিটি শর্ত পরীক্ষা করে দেখানো যায়। তারপর নিজের সমাধান model answer-এর সাথে মেলান; এই পর্বের simulator বদলে নিজের নকশাও চালিয়ে দেখতে পারেন।

  1. পথচারী পারাপারের ধাপ যোগ করুন (circuit লিখুন): প্রধান সড়ক পার হওয়ার জন্য একটি পথচারী বোতাম আর একটি WALK বাতি যোগ করুন। Acceptance criteria: (ক) প্রধান সড়ক সবুজ থাকাকালীন বোতাম চাপলে, ন্যূনতম সবুজ শেষ হওয়ামাত্র হলুদ হয়ে লাল হয়; (খ) WALK জ্বলে শুধু যখন প্রধান সড়ক লাল; (গ) WALK অন্তত ৫ tick থাকে, পার্শ্ব রাস্তায় গাড়ি না থাকলেও; (ঘ) bounce-সহ একটি চাপ একটিই অনুরোধ, আর বোতাম ছেড়ে দিলেও অনুরোধ হারায় না; (ঙ) আগের সব safety check পাস করে। পরিবর্তিত শর্ত, D0-এর সমীকরণ আর অনুরোধ মনে রাখার circuit লিখুন।

    অনুরোধ মনে রাখা: বোতাম → synchroniser → debouncer → edge detector (এক cycle-এর পালস P). একটি flip-flop PR: P = 1 হলে set, আর controller SG-তে ঢোকার মুহূর্তে (next = SG) clear. অনুরোধ R = C + PR — গাড়ি বা পথচারী, যেকোনো একজন অপেক্ষা করলেই হলো।

    নতুন timer শর্ত: TW = (t ≥ 4) — ৫ tick-এর WALK. শর্ত বদল: MG থেকে বেরোনো R·TL (C·TL-এর জায়গায়); SG থেকে বেরোনো TL + C′·TW (আগে C′ + TL) — গাড়ি না থাকলেও অন্তত ৫ tick. WALK = SG = Q1Q0 (Moore). পথচারী প্রধান সড়ক পার হন পার্শ্ব রাস্তার গাড়ির সাথে একই সময়ে — দুটি পথ পরস্পরকে কাটে না।

    সমীকরণ: D1 আগের মতোই (Q0·TS + Q1·TS′)। SG-তে থাকার শর্ত এখন (TL + C′TW)′ = TL′·(C + TW′), তাই D0 = Q1′Q0 + Q1′·R·TL + Q0·C·TL′ + Q0·TL′·TW′. Scratch script-এ ৪টি state × ৩২টি input-সমাহারে এটি নতুন state table-এর সাথে ১২৮/১২৮ মিলেছে, আর minimiser-ও একই চারটি term দিয়েছে।

    পরীক্ষা: গাড়ি ছাড়া, tick ৪ আর ৩০-এ বোতাম চেপে simulate করলে WALK জ্বলে tick ১১–১৫ আর ৩৪–৩৮ — প্রতিবার ঠিক ৫ tick, আর প্রতিবার প্রধান সড়ক লাল।

  2. দাম ২০ টাকা করুন (নকশা, numerical): একই ৫ আর ১০ টাকা, বাতিল বোতাম রেখে vending machine-এর দাম ২০ টাকা করুন। Acceptance criteria: ৫+৫+৫+৫, ১০+১০, ৫+১০+৫, ১০+৫+৫-এ পণ্য, ভাংতি নেই; ১৫ জমা থাকা অবস্থায় ১০ এলে পণ্য আর ৫ টাকা ভাংতি; বাতিলে পুরো জমা ফেরত; টাকার হিসাব সবসময় মেলে। কয়টি state আর flip-flop লাগবে? Mealy state table আর D1, D0, VEND, CHG5-এর ন্যূনতম সমীকরণ দিন।

    State: S0, S5, S10, S15 — ৪টি, binary-তে ২টি flip-flop (00, 01, 10, 11). এবার কোনো unused code নেই — lock-up-এর ভয় নেই, কিন্তু don't care-এর সুবিধাও কম (শুধু অসম্ভব input-সমাহার)।

    state | -      N (5)       D (10)            X
    S0    | S0     S5          S10               S0
    S5    | S5     S10         S15               S0 / RET
    S10   | S10    S15         S0 / VEND         S0 / RET
    S15   | S15    S0 / VEND   S0 / VEND, CHG5   S0 / RET

    সমীকরণ (minimiser দিয়ে যাচাই করা): VEND = Q1·D + Q1Q0·N, CHG5 = Q1Q0·D, RET = (Q1 + Q0)·X, D1 = Q1′·D + Q1·N′D′X′ + N·(Q1 ⊕ Q0) (minimiser-এর Q1′Q0N + Q1Q0′N দুটি term একসাথে লিখলে XOR), D0 = Q0′·N + Q0·N′D′X′ + Q1′Q0·D.

    যাচাইয়ের নমুনা: S10 + N → S15: D1 = N·(1 ⊕ 0) = 1, D0 = Q0′N = 1 → 11 ✓. S15 + D → S0: D1 = 0 + 0 + 0 = 0, D0 = 0 + 0 + Q1′Q0D = 0; VEND = 1, CHG5 = 1 ✓ (১৫ + ১০ = ২৫ = ২০ + ৫)। S10 + D → S0, VEND = 1, CHG5 = Q1Q0D = 0 ✓ (১০ + ১০ = ২০, ভাংতি নেই)।

  3. সর্বনিম্ন clock period (trace-by-hand numerical): অন্য একটি প্রযুক্তিতে tclk→Q = ০.৮ ns, NOT = ০.৩, AND = ০.৬, OR = ০.৭, XOR = ০.৯ ns, tsetup = ০.৫ ns. Traffic controller-এর (ক) timer → TL → TL′ → D0 পথের delay, (খ) সেই পথ থেকে ST (দুটি XOR-এর OR) হয়ে timer-এর clear (NOT + AND) পর্যন্ত পথের delay, (গ) Tmin, fmax, আর ১০০ MHz clock-এ slack বের করুন। (ঘ) বাস্তবের মতো ৩০ tick-এর ন্যূনতম সবুজের জন্য timer-এ কত বিট লাগবে, আর TL-এর বিট-শর্ত কী?

    (ক) ০.৮ (clk→Q) + ০.৬ + ০.৭ (TL = t3 + t2t1t0) + ০.৩ (NOT) + ০.৬ (AND Q0·C·TL′) + ০.৭ (OR) + ০.৫ (setup) = ৪.২ ns.

    (খ) D0 তৈরি ৩.৭ ns-এ → XOR ৪.৬ → OR ৫.৩ (ST) → NOT ৫.৬ → AND ৬.২ → setup যোগে ৬.৭ ns.

    (গ) Tmin = ৬.৭ ns (দুটির বড়টি), fmax = ১ ÷ ৬.৭ ns ≈ ১৪৯.৩ MHz. ১০০ MHz-এর period ১০ ns, slack = ১০ − ৬.৭ = ৩.৩ ns — চলবে।

    (ঘ) ৩০ tick থাকতে TL = (t ≥ 29). t-কে অন্তত ২৯ পর্যন্ত গুনতে হবে, আর saturate করতে হবে তার ওপরে — ৫ বিট (০–৩১) যথেষ্ট, ৪ বিট (০–১৫) নয়। ২৯ = 11101, তাই t ≥ 29 মানে t = 11101, 11110 বা 11111: TL = t4·t3·t2·(t1 + t0). TL-এর gate বদলেছে বলে (ক) আর (খ) নতুন করে হিসাব করতে হবে — review-তে timing সবসময় শেষ নকশায় আবার মাপা হয়।

  4. জমা টাকার display (circuit লিখুন, তারপর output অনুমান করুন): ১৫ টাকার vending machine-এ দুটি common-cathode 7-segment display (segment a–g, 1 = জ্বলে) যোগ করুন, যা জমা টাকা দেখাবে: S0-তে “ 0”, S5-তে “ 5”, S10-তে “10” (বাঁয়ের অঙ্ক না লাগলে নিভে থাকবে)। Acceptance criteria: display শুধু state থেকে (Moore), state বদলের একই clock-এর ধারে বদলায়, আর কোনো বৈধ state-এ ভুল অঙ্ক দেখায় না। দুই অঙ্কের প্রতিটি segment-এর সমীকরণ Q1, Q0 দিয়ে লিখুন। তারপর ৫, ৫, ৫ ক্রমে প্রতিটি clock-এর ধারের পরে display কী দেখাবে, অনুমান করুন। State 11-এ display কী দেখাবে?

    ডানের অঙ্ক শুধু ০ বা ৫. “0” = a b c d e f, “5” = a c d f g. চারটি segment (a, c, d, f) দুটিতেই জ্বলে → সবসময় 1. b আর e শুধু “0”-তে, g শুধু “5”-তে। “5” দেখায় শুধু S5-এ (Q0 = 1): a = c = d = f = 1, b = e = Q0′, g = Q0.

    বাঁয়ের অঙ্ক শুধু S10-এ “1” (segment b, c), বাকি সময় নিভে: b = c = Q1, বাকি সব 0. পর্ব ১২-এর BCD-to-7-segment decoder-ও চলত, কিন্তু তিনটি state-এর জন্য দুটি তার আর একটি NOT অনেক সস্তা।

    অনুমান, ৫, ৫, ৫: প্রথম ধারের পরে S5 → “ 5”; দ্বিতীয়ের পরে S10 → “10”; তৃতীয় ৫ ঢোকার cycle-এ VEND আর পরের ধারে S0 → “ 0”. ১৫ কখনো দেখায় না, কারণ Mealy নকশায় ১৫-এর কোনো state নেই। State 11-এ: Q0 = 1 বলে ডানে “5”, Q1 = 1 বলে বাঁয়ে “1” — display দেখাবে “15”! একটি অসম্ভব সংখ্যা চোখে পড়লে টেকনিশিয়ান বুঝবেন FSM অবৈধ state-এ ঢুকেছে — display এখানে বিনা খরচের একটি diagnostic.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি FSM-ভিত্তিক controller প্রজেক্টের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. পরীক্ষাযোগ্য requirement আর ধরে নেওয়া শর্ত লেখা
  2. State diagram আঁকা আর প্রতিটি state-এর তীরের সম্পূর্ণতা যাচাই
  3. State table বানিয়ে সমতুল্য state মেলানো (minimisation)
  4. State assignment বেছে K-map থেকে next-state আর output সমীকরণ বের করা
  5. RTL আর testbench লিখে simulate করা, তারপর timing আর resource review

১০টি state-এর একটি FSM-কে binary encoding আর one-hot encoding-এ বানাতে যথাক্রমে কমপক্ষে কতটি flip-flop লাগবে?

  • ৩ আর ১০
  • ৪ আর ৫
  • ৪ আর ১০
  • ১০ আর ৪

একটি traffic controller-এ প্রতি সেকেন্ডে একটি tick আসে। Timer t প্রতিটি tick-এ ১ বাড়ে, আর state বদলালে পরের tick-এ ০ থেকে শুরু করে (state-এর প্রথম tick-এ t = 0). প্রধান সড়কের ন্যূনতম সবুজ যাতে ঠিক ৩০ সেকেন্ড হয়, বেরোনোর শর্তে কোন timeout ব্যবহার করবেন?

  • t ≥ 30
  • t = 30
  • t ≥ 29
  • t > 30

একটি FSM-এ flip-flop-এর clock-to-Q delay ১ ns, সবচেয়ে লম্বা combinational পথ ৩.৫ ns, আর setup time ০.৫ ns (clock skew শূন্য)। সর্বোচ্চ clock কম্পাঙ্ক কত?

  • ২০০ MHz
  • প্রায় ২২২ MHz
  • প্রায় ২৮৬ MHz
  • ২ GHz

একটি Mealy vending machine-এ পণ্যের দাম ১৫ টাকা; state মানে জমা টাকা (S0, S5, S10). যন্ত্রটি S10-এ থাকা অবস্থায় একটি ১০ টাকা ঢুকলে সেই cycle-এ output আর পরের state কী?

  • কোনো output নয়, পরের state S20
  • VEND = 1, CHG5 = 0, পরের state S0
  • এই cycle-এ কিছু নয়; পরের cycle-এ VEND
  • VEND = 1, CHG5 = 1, পরের state S0

রাস্তার গাড়ি-সেন্সরের সংকেত একটি FSM-এ দেওয়ার আগে কেন দুই flip-flop-এর synchroniser-এর ভেতর দিয়ে নেওয়া হয়?

  • সেন্সরের যান্ত্রিক লাফানো (bounce) দূর করতে
  • সংকেতটি asynchronous, তাই metastability-র ঝুঁকি কমাতে
  • Clock-এর কম্পাঙ্ক অর্ধেক করতে
  • FSM-কে Mealy থেকে Moore বানাতে

একটি traffic FSM-এর state code Q1Q0, আর next-state সমীকরণ D1 = Q0·TS + Q1·TS′. এই সমীকরণটি কোন একটি block দিয়ে সরাসরি বানানো যায়?

  • একটি 2:1 multiplexer, select = TS
  • একটি XOR gate
  • একটি 2:4 decoder
  • একটি priority encoder

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. প্রজেক্টের পথ: পরীক্ষাযোগ্য requirement আর লিখিত assumption → state diagram (সম্পূর্ণতা যাচাই) → state table → minimisation → state assignment → সমীকরণ → RTL → testbench → review.
  2. Controller + datapath (FSMD): FSM সিদ্ধান্ত নেয়, counter গোনে; কথা হয় ST আর TS/TL-এর মতো কয়েকটি সংকেতে। Timer-এর শর্ত t ≥ k − 1, যাতে state ঠিক k tick থাকে।
  3. Gray-ক্রমের encoding চক্রাকার FSM-এ প্রতিটি পদক্ষেপে একটি বিট বদলায়: D1 = Q0·TS + Q1·TS′, D0 = Q1′Q0 + Q1′·C·TL + Q0·C·TL′; one-hot-এ বেশি flip-flop, সরল সমীকরণ।
  4. Vending machine: Mealy-তে ৩ state, Moore-এ ৫; CHG5 = Q1·D; টাকার হিসাব সবসময় মেলাতে হবে; don't care দিয়ে সরল করা logic unused state-এ বিপদ ঘটাতে পারে — reset আর default লাগবে।
  5. Review: safety assertion, transition coverage, critical path থেকে Tmin, দ্রুত clock + tick enable, প্রতিটি বাইরের input-এ synchroniser ও debounce, আর বোর্ডের manual থেকে pin.

পরের পর্বে: নতুন মডিউল — কম্পিউটারের গাণিতিক কাজ। প্রথমেই integer যোগ-বিয়োগ: একটি adder-ই কীভাবে subtractor হয়, overflow কখন আর কেন ঘটে, আর CPU-র zero, negative, carry, overflow flag কীভাবে তৈরি হয়।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান