blob: 38cdafe243c520355c81ca6846b50e128553b758 [file] [log] [blame]
`default_nettype none
// Top-level design module, acting only as a wrapper
module zymason_tinytop (
input logic [7:0] io_in,
output logic [7:0] io_out);
Zymason_Tiny1 p0 (.clock(io_in[0]), .reset(io_in[1]), .RW(io_in[2]),
.sel(io_in[3]), .pin_in(io_in[7:4]), .io_out);
endmodule : zymason_tinytop
// Primary design module
module Zymason_Tiny1 (
input logic clock, reset,
input logic RW, sel,
input logic [3:0] pin_in,
output tri [7:0] io_out);
localparam NUM_DIGITS = 12; // The number of total digits that can be stored
logic [6:0] dig_out[NUM_DIGITS-1:0]; // Unpacked digit output array
logic [NUM_DIGITS-1:0] dig_en; // Enable line for each digit
logic pos_en, pulse;
// Shift register for selecting current display digit in both modes
// Control FSM
// Clocking module to generate slow pulses for display cycling in R-mode
Zymason_ShiftReg #(NUM_DIGITS) s0 (.clock, .reset, .en(pos_en), .out(dig_en));
Zymason_FSM f0 (.clock, .reset, .RW, .sel, .pulse, .pos_en);
Zymason_PulseGen p0 (.clock, .reset, .spd({pin_in, sel}), .pulse);
genvar i;
generate
for (i=0; i<NUM_DIGITS; i=i+1) begin: STR
Zymason_DigStore ds (.clock, .reset, .en(dig_en[i]), .sel, .RW, .pin_in,
.dig_out(dig_out[i]));
Zymason_Drive dr (.en(dig_en[i]), .val(dig_out[i]), .out(io_out[6:0]));
end
endgenerate
// Mode indicator
assign io_out[7] = RW;
endmodule : Zymason_Tiny1
module Zymason_Drive (
input logic en,
input logic [6:0] val,
output tri [6:0] out);
assign out = en ? val : 7'bz;
endmodule : Zymason_Drive
// Control state machine for Zymason_Tiny1
module Zymason_FSM (
input logic clock, reset,
input logic RW, sel, pulse,
output logic pos_en);
// assign st_out = state;
enum logic [1:0] {INIT, SCAN, WRT0, WRT1} state, nextState;
// Explicit-style FSM
always_ff @(posedge clock, posedge reset) begin
if (reset)
state <= INIT;
else
state <= nextState;
end
// Next-state logic
always_comb begin
case (state)
INIT: nextState = RW ? WRT0 : SCAN;
SCAN: nextState = RW ? WRT0 : SCAN;
WRT0: nextState = sel ? WRT1 : WRT0;
WRT1: nextState = RW ? (sel ? WRT1 : WRT0) : SCAN;
default: nextState = INIT;
endcase
end
// Output logic
always_comb begin
case (state)
INIT: pos_en = 1'b0;
SCAN: pos_en = ~RW & pulse;
WRT0: pos_en = 1'b0;
WRT1: pos_en = RW & ~sel;
default: pos_en = 1'b0;
endcase
end
endmodule : Zymason_FSM
// Single digit storage instance
module Zymason_DigStore (
input logic clock, reset,
input logic en, sel, RW,
input logic [3:0] pin_in,
output logic [6:0] dig_out);
// 2 implicit registers with a synchronous reset
always_ff @(posedge clock, posedge reset) begin
if (reset)
dig_out <= 7'd0;
else begin
if (en & ~sel & RW)
dig_out[3:0] <= pin_in;
else if (en & sel & RW)
dig_out[6:4] <= pin_in[2:0];
end
end
endmodule : Zymason_DigStore
// Read-only left-shift register that resets to ...0001
module Zymason_ShiftReg
#(parameter DW = 2)
(input logic clock, reset,
input logic en,
output logic [DW-1:0] out);
logic [DW:0] long_out;
logic tmp;
assign out = long_out[DW-1:0];
assign tmp = long_out[DW-1];
always_ff @(posedge clock, posedge reset) begin
if (reset) begin
long_out <= 1;
end
else if (en) begin
long_out <= {long_out, tmp};
end
end
endmodule : Zymason_ShiftReg
// Internal clocking pulse, expecting 6.25kHz clock as input
module Zymason_PulseGen (
input logic clock, reset,
input logic [4:0] spd,
output logic pulse);
logic [8:0] count;
logic [4:0] lowCount;
logic en_low;
logic temp_pulse;
// Invariant counter to produce pulses at 12.1Hz
always_ff @(posedge clock) begin
if (reset)
count <= 9'd0;
else
count <= count + 9'd1;
end
// Variable counter to find spd
always_ff @(posedge clock) begin
if (reset | pulse)
lowCount <= 5'd0;
else if (en_low & spd[0])
lowCount <= lowCount + 5'd1;
end
// pulse is asserted for a single cycle since its counter immediately resets
assign pulse = ((lowCount[4:1] == spd[4:1]) & spd[0]) ? en_low : 1'b0;
assign en_low = (count == 9'd0) ? 1'b1 : 1'b0;
endmodule : Zymason_PulseGen