Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Digital Stopwatch Controller

## Description
This project implements a digital stopwatch using Verilog RTL and a C++ control program. The design is modular, separating the control logic from the timekeeping counters.

## Project Structure
- **rtl/**: Contains the synthesizable Verilog files (Top-level, FSM, and Counters).
- **verilator_sw/**: Contains the C++ test harness and the Makefile for the Verilator build flow.
- **tb/**: Contains the standard Verilog testbench for logic verification.

## Design Choices
- **Synchronous Logic**: To ensure reliability, all counters are synchronous. Ripple counters were not used to avoid timing hazards.
- **FSM-Based Control**: A 3-state Finite State Machine (IDLE, RUNNING, PAUSED) ensures the stopwatch only counts when explicitly started.
- **Modular Hierarchy**: The design is split into separate modules for the FSM, seconds counter, and minutes counter to ensure clarity and maintainability.

## How to Run
1. Navigate to the `verilator_sw/` directory.
2. Run `make`.
3. The program will compile the RTL into a C++ model and execute the simulation sequence, printing the time in MM:SS format.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module control_fsm (
input wire clk,
input wire rst_n,
input wire start,
input wire stop,
input wire reset,
output reg [1:0] state_out,
output wire count_en
);

parameter IDLE = 2'b00, RUNNING = 2'b01, PAUSED = 2'b10;
reg [1:0] state, next_state;

// Sequential state update
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else if (reset)
state <= IDLE;
else
state <= next_state;
end

// Next state logic
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = stop ? PAUSED : RUNNING;
PAUSED: next_state = start ? RUNNING : PAUSED;
default: next_state = IDLE;
endcase
end

assign count_en = (state == RUNNING);

always @(*) begin
state_out = state;
end

endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module minutes_counter (
input wire clk,
input wire rst_n,
input wire reset,
input wire en,
output reg [7:0] count
);

always @(posedge clk or negedge rst_n) begin
if (!rst_n || reset) begin
count <= 8'd0;
end else if (en) begin
if (count == 8'd99)
count <= 8'd0;
else
count <= count + 1'b1;
end
end

endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module seconds_counter (
input wire clk,
input wire rst_n,
input wire reset,
input wire en,
output reg [5:0] count,
output wire rollover
);

always @(posedge clk or negedge rst_n) begin
if (!rst_n || reset) begin
count <= 6'd0;
end else if (en) begin
if (count == 6'd59)
count <= 6'd0;
else
count <= count + 1'b1;
end
end

assign rollover = en && (count == 6'd59);

endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module stopwatch_top (
input wire clk,
input wire rst_n,
input wire start,
input wire stop,
input wire reset,
output wire [7:0] minutes,
output wire [5:0] seconds,
output wire [1:0] status
);

wire count_en;
wire sec_done;

// FSM instance
control_fsm fsm (
.clk(clk), .rst_n(rst_n), .start(start),
.stop(stop), .reset(reset), .state_out(status), .count_en(count_en)
);

// Seconds counter
seconds_counter sec_cnt (
.clk(clk), .rst_n(rst_n), .reset(reset),
.en(count_en), .count(seconds), .rollover(sec_done)
);

// Minutes counter
minutes_counter min_cnt (
.clk(clk), .rst_n(rst_n), .reset(reset),
.en(sec_done), .count(minutes)
);

endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
`timescale 1ns/1ps
module tb_stopwatch();
reg clk=0, rst_n=0, start=0, stop=0, reset=0;
wire [7:0] mins; wire [5:0] secs; wire [1:0] stat;

stopwatch_top uut (clk, rst_n, start, stop, reset, mins, secs, stat);

always #5 clk = ~clk;

initial begin
$dumpfile("stopwatch.vcd");
$dumpvars(0, tb_stopwatch);

#20 rst_n = 1;
#20 start = 1; #10 start = 0;
#1000 stop = 1; #10 stop = 0;
#100 $finish;
end
endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Quick build script
all:
verilator -Wall --cc ../rtl/stopwatch_top.v --exe main.cpp
make -C obj_dir -f Vstopwatch_top.mk Vstopwatch_top
./obj_dir/Vstopwatch_top

clean:
rm -rf obj_dir
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <iostream>
#include <iomanip>
#include "Vstopwatch_top.h"
#include "verilated.h"

int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vstopwatch_top* top = new Vstopwatch_top;

// Helper to pulse the clock
auto tick = [&]() {
top->clk = 1; top->eval();
top->clk = 0; top->eval();
};

// System Startup
top->rst_n = 0; tick();
top->rst_n = 1; tick();

// Press Start
top->start = 1; tick();
top->start = 0;

std::cout << "Starting Simulation..." << std::endl;

for (int i = 0; i < 500; i++) {
tick();
if (i % 20 == 0) {
std::cout << "Current Time: "
<< std::setw(2) << std::setfill('0') << (int)top->minutes << ":"
<< std::setw(2) << std::setfill('0') << (int)top->seconds
<< " [Status: " << (int)top->status << "]" << std::endl;
}
}

delete top;
return 0;
}