// ============================================================================ // f190.v — 54F/74F190 Up/Down Decade Counter (with Preset and Ripple Clock) // // Fairchild FAST (Advanced Schottky TTL) // Source: docs/devices/54F74F190.txt (1980 Fairchild FAST Data Book, // pages 4-48 ... 4-51, PRELIMINARY) // // Modes of operation, in order of precedence (data sheet Mode Select table): // 1. /PL LOW : asynchronous parallel load — Pn // loaded into Qn immediately, // overriding all other inputs // 2. /PL HIGH, /CE LOW, /U/D LOW : count up on rising CP edge // 3. /PL HIGH, /CE LOW, /U/D HIGH : count down on rising CP edge // 4. /PL HIGH, /CE HIGH : hold (no change) // // Count sequence is BCD (8421): 0..9, then 9 -> 0 counting up and 0 -> 9 // counting down. Illegal states 10-15 recover into the legal sequence. // // TC = HIGH when (count UP and cnt=9) or (count DOWN and cnt=0). // // /RC is normally HIGH. When /CE = LOW and TC = HIGH, /RC follows CP: // /RC = CP (same polarity). Otherwise /RC = HIGH. // // Timing values from the data sheet AC Characteristics table, 54F/74F column // (T_A = +25 C, V_CC = +5.0 V, C_L = 15 pF). Prelim page — TYP values only // (Min/Max columns are blank), so each specparam carries the typ value alone. // // Ports are scalar and named after the data sheet pin names: Icarus Verilog // does not fully support multi-bit (parallel) specify path connections, so // vector ports would get incorrect per-bit delays. // ============================================================================ `timescale 1ns/100ps module f190 ( input wire cp, // clock pulse (active rising edge, pin 14) input wire pl_n, // parallel load (asynchronous, active LOW, pin 11) input wire ce_n, // count enable (active LOW, pin 4) input wire ud_n, // up/down count control (LOW=up, pin 5) input wire p0, // parallel data input 0 (pin 15) input wire p1, // parallel data input 1 (pin 1) input wire p2, // parallel data input 2 (pin 10) input wire p3, // parallel data input 3 (pin 9) output wire q0, // flip-flop output 0 (pin 3) output wire q1, // flip-flop output 1 (pin 2) output wire q2, // flip-flop output 2 (pin 6) output wire q3, // flip-flop output 3 (pin 7) output wire tc, // terminal count (active HIGH, pin 12) output wire rc_n // ripple clock (active LOW, pin 13) ); // Count sequences, per the State Diagram. Counting up, the illegal // states recover along 10 -> 11 -> 6, 12 -> 13 -> 4 and 14 -> 15 -> 2; // counting down they chain 15 -> ... -> 10 -> 9 into the legal sequence, // which is a plain decrement. function [3:0] count_up; input [3:0] c; case (c) 4'd9: count_up = 4'd0; 4'd10: count_up = 4'd11; 4'd11: count_up = 4'd6; 4'd12: count_up = 4'd13; 4'd13: count_up = 4'd4; 4'd14: count_up = 4'd15; 4'd15: count_up = 4'd2; default: count_up = c + 4'd1; endcase endfunction function [3:0] count_dn; input [3:0] c; count_dn = (c == 4'd0) ? 4'd9 : c - 4'd1; endfunction // /PL idles HIGH, hence the previous-level register's initial value. A // CP edge arriving while /PL is LOW is consumed by the load branch, which // has priority, and leaves nothing pending; a CP edge after the load // counts normally, since only a genuine rising edge fires the block. reg [3:0] state; reg pl_d = 1'b1; always @(posedge cp or posedge pl_n or negedge pl_n) begin if (!pl_n || !pl_d) state <= {p3, p2, p1, p0}; else if (!ce_n) state <= ud_n ? count_dn(state) : count_up(state); pl_d <= pl_n; end // /PL passes P straight to the outputs, overriding the state register, // which is what makes the load transparent to P while /PL is LOW. The // `!pl_d` term above recaptures P into the state register when /PL is // released. wire [3:0] cnt = !pl_n ? {p3, p2, p1, p0} : state; assign q0 = cnt[0]; assign q1 = cnt[1]; assign q2 = cnt[2]; assign q3 = cnt[3]; wire tc_int; assign tc_int = (!ud_n && cnt == 4'd9) || (ud_n && cnt == 4'd0); assign tc = tc_int; assign rc_n = (!ce_n && tc_int) ? cp : 1'b1; specify specparam tlh_cp_q = 4.5; specparam thl_cp_q = 5.5; specparam tlh_cp_tc = 6.5; specparam thl_cp_tc = 8.5; specparam tlh_cp_rc = 4.5; specparam thl_cp_rc = 4.0; specparam tlh_ce_rc = 3.6; specparam thl_ce_rc = 3.5; specparam tlh_ud_rc = 10.0; specparam thl_ud_rc = 8.0; specparam tlh_ud_tc = 5.0; specparam thl_ud_tc = 5.5; specparam tlh_pn_q = 3.6; specparam thl_pn_q = 6.3; specparam tlh_pl_q = 5.7; specparam thl_pl_q = 6.2; specparam tlh_pl_tc = 5.7; specparam thl_pl_tc = 6.2; specparam tlh_pl_rc = 5.7; specparam thl_pl_rc = 6.2; (cp => q0) = (tlh_cp_q, thl_cp_q); (cp => q1) = (tlh_cp_q, thl_cp_q); (cp => q2) = (tlh_cp_q, thl_cp_q); (cp => q3) = (tlh_cp_q, thl_cp_q); (cp => tc) = (tlh_cp_tc, thl_cp_tc); (cp => rc_n) = (tlh_cp_rc, thl_cp_rc); (ce_n => rc_n) = (tlh_ce_rc, thl_ce_rc); (ud_n => tc) = (tlh_ud_tc, thl_ud_tc); (ud_n => rc_n) = (tlh_ud_rc, thl_ud_rc); (p0 => q0) = (tlh_pn_q, thl_pn_q); (p1 => q1) = (tlh_pn_q, thl_pn_q); (p2 => q2) = (tlh_pn_q, thl_pn_q); (p3 => q3) = (tlh_pn_q, thl_pn_q); (pl_n => q0) = (tlh_pl_q, thl_pl_q); (pl_n => q1) = (tlh_pl_q, thl_pl_q); (pl_n => q2) = (tlh_pl_q, thl_pl_q); (pl_n => q3) = (tlh_pl_q, thl_pl_q); (pl_n => tc) = (tlh_pl_tc, thl_pl_tc); (pl_n => rc_n) = (tlh_pl_rc, thl_pl_rc); `ifndef __ICARUS__ specparam ts_ph = 5.0; specparam ts_pl = 5.0; specparam th_ph = 3.0; specparam th_pl = 3.0; specparam ts_ce = 10.0; specparam th_ce = 0.0; specparam tw_pl_l = 5.0; specparam tw_cp_l = 5.5; specparam trec_pl = 6.0; $setup(p0, posedge pl_n, ts_ph); $setup(p1, posedge pl_n, ts_ph); $setup(p2, posedge pl_n, ts_ph); $setup(p3, posedge pl_n, ts_ph); $hold(posedge pl_n, p0, th_ph); $hold(posedge pl_n, p1, th_ph); $hold(posedge pl_n, p2, th_ph); $hold(posedge pl_n, p3, th_ph); $setup(ce_n, posedge cp, ts_ce); $hold(posedge cp, ce_n, th_ce); $width(negedge pl_n, tw_pl_l); $width(negedge cp, tw_cp_l); $recovery(posedge pl_n, posedge cp, trec_pl); `endif endspecify endmodule