This project is a simulation of a traffic light controller using Verilog for the logic and Three.js for the 3D visualization. The traffic light system controls two streets: Academic Ave and Bravado Blvd, featuring real-time state transitions (green, yellow, and red lights) based on traffic presence. The Verilog module handles the state transitions and timing, while the Three.js visualization provides an 3D representation of the traffic lights, including an overlay to display the current light state with countdown timers.
This project is inspired by concepts from Sara Harris's book "Digital Design and Computer Architecture".
- Traffic Light Controller Simulation with Verilog 🚦
- Attribution
- Demo Video
- Table of Contents
- Installation
- Usage
- Code Explanation
- TrafficLightController Module :
- TrafficLightController Testbench
- GTKWave Simulation
- Traffic Light Simulation with Three.js
- Initialize Scene, Camera, and Renderer
- Orbit Controls for Interactive Camera Movement
- Adjust Camera Settings
- Handle Window Resizing
- Add Ambient and Directional Light
- Light Colors
- Create Traffic Light with Housing and Pole
- Initialize Two Traffic Lights
- UI Overlay for State Information
- State Machine Variables
- Function to Update Traffic Lights and Overlay Text
- Animation Loop
To run this project locally:
-
Clone the repository and cd into it :
cd Lab-Project
-
Open your browser and go to localhost.
After starting the development server, the simulation will automatically run.
module TrafficLightController(
input wire clk,
input wire reset,
input wire traffic_A,
input wire traffic_B,
output reg [1:0] LA,
output reg [1:0] LB
);
- Defines the module and its inputs/outputs.
parameter S0 = 2'b00;
parameter S1 = 2'b01;
parameter S2 = 2'b10;
parameter S3 = 2'b11;
- Defines the states as numerical values.
reg [1:0] state, next_state;
integer yellow_timer;
- Declares internal registers for the current state, next state, and a timer for yellow light states.
always @(posedge clk or posedge reset) begin
if (reset) begin
state <= S0;
yellow_timer <= 0;
end else begin
state <= next_state;
end
end
- Sets the initial state and resets the timer on reset signal. Otherwise, updates the state to the next state on each clock cycle.
always @(*) begin
case (state)
S0: begin
LA = 2'b10;
LB = 2'b00;
if (!traffic_A)
next_state = S1;
else
next_state = S0;
end
S1: begin
LA = 2'b01;
LB = 2'b00;
if (yellow_timer >= 5)
next_state = S2;
else
next_state = S1;
end
S2: begin
LA = 2'b00;
LB = 2'b10;
if (!traffic_B)
next_state = S3;
else
next_state = S2;
end
S3: begin
LA = 2'b00;
LB = 2'b01;
if (yellow_timer >= 5)
next_state = S0;
else
next_state = S3;
end
default: begin
next_state = S0;
end
endcase
end
- Determines the next state and output lights based on the current state and traffic presence.
always @(posedge clk or posedge reset) begin
if (reset) begin
yellow_timer <= 0;
end else if (state == S1 || state == S3) begin
yellow_timer <= yellow_timer + 1;
end else begin
yellow_timer <= 0;
end
end
- Resets the timer on reset or when the state is not
S1
orS3
. Increments the timer on each clock cycle when the state isS1
orS3
.
This Verilog testbench is used to simulate and verify the functionality of the TrafficLightController
module.
`timescale 1ns / 1ps
`include "TrafficLightController.v"
- Sets the timescale for the simulation and includes the
TrafficLightController
module.
module TrafficLightController_tb();
reg clk;
reg reset;
reg traffic_A;
reg traffic_B;
wire [1:0] LA;
wire [1:0] LB;
- Defines the testbench module and its input/output signals.
TrafficLightController UUT (
.clk(clk),
.reset(reset),
.traffic_A(traffic_A),
.traffic_B(traffic_B),
.LA(LA),
.LB(LB)
);
- Instantiates the
TrafficLightController
module and connects the testbench signals to the module's ports.
initial begin
clk = 0;
forever #5 clk = ~clk;
end
- Generates a clock signal with a period of 10 nanoseconds.
integer file;
initial begin
$dumpfile("TrafficLightController_tb.vcd");
$dumpvars(0, TrafficLightController_tb);
file = $fopen("traffic_data.csv", "w");
$fwrite(file, "time,traffic_A,traffic_B,LA,LB\n");
- Configures waveform logging for GTKWave and opens a CSV file for writing simulation data.
reset = 1;
traffic_A = 0;
traffic_B = 0;
#10;
reset = 0;
#10;
- Activates the reset signal and deactivates traffic signals initially. Then deactivates the reset signal after 10 nanoseconds.
traffic_A = 1;
#50;
$fwrite(file, "%0d,%0d,%0d,%0d,%0d\n", $time, traffic_A, traffic_B, LA, LB);
- Activates traffic on Academic Ave and logs the state after 50 nanoseconds.
traffic_A = 0;
traffic_B = 1;
#50;
$fwrite(file, "%0d,%0d,%0d,%0d,%0d\n", $time, traffic_A, traffic_B, LA, LB);
- Deactivates traffic on Academic Ave and activates traffic on Bravado Blvd. Logs the state after 50 nanoseconds.
traffic_B = 0;
#50;
$fwrite(file, "%0d,%0d,%0d,%0d,%0d\n", $time, traffic_A, traffic_B, LA, LB);
- Deactivates traffic on Bravado Blvd and logs the state after 50 nanoseconds.
#10 $finish;
$fclose(file);
end
endmodule
- Ends the simulation after 10 nanoseconds and closes the CSV file.
GTKWave is a waveform viewer that can be used to visualize the simulation results of the TrafficLightController
module. The testbench generates a VCD (Value Change Dump) file that can be opened with GTKWave to analyze the behavior of the traffic light controller over time.
- Run the simulation to generate the
TrafficLightController_tb.vcd
file. - Open GTKWave and load the
TrafficLightController_tb.vcd
file. - Add the relevant signals (
clk
,reset
,traffic_A
,traffic_B
,LA
,LB
) to the waveform viewer. - Analyze the waveforms to verify the correct operation of the traffic light controller.
- The image above shows the GTKWave simulation, where you can see the state transitions and the corresponding traffic light outputs.
By using GTKWave, you can visually inspect the timing and state changes of the traffic light controller, ensuring that it operates as expected under different traffic conditions.
This JavaScript code uses the Three.js library to create a 3D simulation of a traffic light system repesnting the verilog code logic.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
- Initializes the scene, camera, and renderer. Sets the background color and appends the renderer to the document body.
let controls;
try {
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
} catch (e) {
console.warn("OrbitControls not found. Skipping camera controls.");
}
- Adds OrbitControls for interactive camera movement, with error handling if OrbitControls is not found.
camera.position.set(0, 2, 5);
if (controls) controls.target.set(0, 0, 0);
- Sets the camera position and target.
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
- Updates the camera and renderer settings when the window is resized.
const ambientLight = new THREE.AmbientLight(0x404040, 1.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(5, 10, 7.5).normalize();
scene.add(directionalLight);
- Adds ambient and directional light to the scene.
const colors = {
green: 0x00ff00,
yellow: 0xffff00,
red: 0xff0000,
off: 0x333333,
};
- Defines the colors for the traffic lights.
function createTrafficLight(x, y, z) {
const group = new THREE.Group();
const greenLight = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshStandardMaterial({ color: colors.off })
);
greenLight.position.set(0, 0.5, 0);
const yellowLight = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshStandardMaterial({ color: colors.off })
);
yellowLight.position.set(0, 0, 0);
const redLight = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshStandardMaterial({ color: colors.off })
);
redLight.position.set(0, -0.5, 0);
group.add(greenLight, yellowLight, redLight);
const housing = new THREE.Mesh(
new THREE.BoxGeometry(0.5, 1.5, 0.5),
new THREE.MeshStandardMaterial({ color: 0x222222 })
);
housing.position.set(0, 0, -0.3);
const pole = new THREE.Mesh(
new THREE.CylinderGeometry(0.1, 0.1, 3),
new THREE.MeshStandardMaterial({ color: 0x333333 })
);
pole.position.set(0, -2, -0.3);
group.add(housing, pole);
group.position.set(x, y, z);
scene.add(group);
return { greenLight, yellowLight, redLight };
}
- Creates a traffic light with housing and pole, and adds it to the scene.
const LA = createTrafficLight(-1, 0, 0);
const LB = createTrafficLight(1, 0, 0);
- Initializes two traffic lights at specified positions.
const overlay = document.createElement("div");
overlay.style.position = "fixed";
overlay.style.top = "10px";
overlay.style.left = "10px";
overlay.style.padding = "10px 15px";
overlay.style.backgroundColor = "rgba(50, 50, 50, 0.8)";
overlay.style.color = "white";
overlay.style.fontSize = "16px";
overlay.style.fontFamily = "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif";
overlay.style.borderRadius = "8px";
overlay.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.4)";
overlay.style.zIndex = "1000";
overlay.style.transition = "all 0.3s ease";
document.body.appendChild(overlay);
- Creates a UI overlay to display state information.
let state = 0;
const yellowDuration = 1.5;
const greenDuration = 2.5;
const clock = new THREE.Clock();
let elapsed = 0;
- Defines state machine variables and durations for yellow and green lights.
function updateTrafficLights() {
LA.greenLight.material.color.set(colors.off);
LA.yellowLight.material.color.set(colors.off);
LA.redLight.material.color.set(colors.off);
LB.greenLight.material.color.set(colors.off);
LB.yellowLight.material.color.set(colors.off);
LB.redLight.material.color.set(colors.off);
let overlayText = "";
switch (state) {
case 0:
LA.greenLight.material.color.set(colors.green);
LB.redLight.material.color.set(colors.red);
overlayText = `Academic Ave: Green (${Math.max(
0,
greenDuration - elapsed
).toFixed(1)}s)`;
if (elapsed > greenDuration) {
state = 1;
elapsed = 0;
}
break;
case 1:
LA.yellowLight.material.color.set(colors.yellow);
LB.redLight.material.color.set(colors.red);
overlayText = `Academic Ave: Yellow (${Math.max(
0,
yellowDuration - elapsed
).toFixed(1)}s)`;
if (elapsed > yellowDuration) {
state = 2;
elapsed = 0;
}
break;
case 2:
LA.redLight.material.color.set(colors.red);
LB.greenLight.material.color.set(colors.green);
overlayText = `Bravado Blvd: Green (${Math.max(
0,
greenDuration - elapsed
).toFixed(1)}s)`;
if (elapsed > greenDuration) {
state = 3;
elapsed = 0;
}
break;
case 3:
LA.redLight.material.color.set(colors.red);
LB.yellowLight.material.color.set(colors.yellow);
overlayText = `Bravado Blvd: Yellow (${Math.max(
0,
yellowDuration - elapsed
).toFixed(1)}s)`;
if (elapsed > yellowDuration) {
state = 0;
elapsed = 0;
}
break;
}
overlay.innerHTML = overlayText;
}
- Updates the traffic lights and overlay text based on the current state and elapsed time.
function animate() {
requestAnimationFrame(animate);
elapsed += clock.getDelta();
updateTrafficLights();
if (controls) controls.update();
renderer.render(scene, camera);
}
- Defines the animation loop to update traffic lights and render the scene.