FPU , I2C , PTC and RTC RTL added to user project area
diff --git a/verilog/rtl/fpu/except.v b/verilog/rtl/fpu/except.v new file mode 100644 index 0000000..007099f --- /dev/null +++ b/verilog/rtl/fpu/except.v
@@ -0,0 +1,153 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// EXCEPT //// +//// Floating Point Exception/Special Numbers Unit //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + + +`timescale 1ns / 100ps + + +module except( clk, opa, opb, inf, ind, qnan, snan, opa_nan, opb_nan, + opa_00, opb_00, opa_inf, opb_inf, opa_dn, opb_dn); +input clk; +input [31:0] opa, opb; +output inf, ind, qnan, snan, opa_nan, opb_nan; +output opa_00, opb_00; +output opa_inf, opb_inf; +output opa_dn; +output opb_dn; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +wire [7:0] expa, expb; // alias to opX exponent +wire [22:0] fracta, fractb; // alias to opX fraction +reg expa_ff, infa_f_r, qnan_r_a, snan_r_a; +reg expb_ff, infb_f_r, qnan_r_b, snan_r_b; +reg inf, ind, qnan, snan; // Output registers +reg opa_nan, opb_nan; +reg expa_00, expb_00, fracta_00, fractb_00; +reg opa_00, opb_00; +reg opa_inf, opb_inf; +reg opa_dn, opb_dn; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign expa = opa[30:23]; +assign expb = opb[30:23]; +assign fracta = opa[22:0]; +assign fractb = opb[22:0]; + +//////////////////////////////////////////////////////////////////////// +// +// Determine if any of the input operators is a INF or NAN or any other special number +// + +always @(posedge clk) + expa_ff <= #1 &expa; + +always @(posedge clk) + expb_ff <= #1 &expb; + +always @(posedge clk) + infa_f_r <= #1 !(|fracta); + +always @(posedge clk) + infb_f_r <= #1 !(|fractb); + +always @(posedge clk) + qnan_r_a <= #1 fracta[22]; + +always @(posedge clk) + snan_r_a <= #1 !fracta[22] & |fracta[21:0]; + +always @(posedge clk) + qnan_r_b <= #1 fractb[22]; + +always @(posedge clk) + snan_r_b <= #1 !fractb[22] & |fractb[21:0]; + +always @(posedge clk) + ind <= #1 (expa_ff & infa_f_r) & (expb_ff & infb_f_r); + +always @(posedge clk) + inf <= #1 (expa_ff & infa_f_r) | (expb_ff & infb_f_r); + +always @(posedge clk) + qnan <= #1 (expa_ff & qnan_r_a) | (expb_ff & qnan_r_b); + +always @(posedge clk) + snan <= #1 (expa_ff & snan_r_a) | (expb_ff & snan_r_b); + +always @(posedge clk) + opa_nan <= #1 &expa & (|fracta[22:0]); + +always @(posedge clk) + opb_nan <= #1 &expb & (|fractb[22:0]); + +always @(posedge clk) + opa_inf <= #1 (expa_ff & infa_f_r); + +always @(posedge clk) + opb_inf <= #1 (expb_ff & infb_f_r); + +always @(posedge clk) + expa_00 <= #1 !(|expa); + +always @(posedge clk) + expb_00 <= #1 !(|expb); + +always @(posedge clk) + fracta_00 <= #1 !(|fracta); + +always @(posedge clk) + fractb_00 <= #1 !(|fractb); + +always @(posedge clk) + opa_00 <= #1 expa_00 & fracta_00; + +always @(posedge clk) + opb_00 <= #1 expb_00 & fractb_00; + +always @(posedge clk) + opa_dn <= #1 expa_00; + +always @(posedge clk) + opb_dn <= #1 expb_00; + +endmodule +
diff --git a/verilog/rtl/fpu/fpu.v b/verilog/rtl/fpu/fpu.v new file mode 100644 index 0000000..245ef20 --- /dev/null +++ b/verilog/rtl/fpu/fpu.v
@@ -0,0 +1,558 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// FPU //// +//// Floating Point Unit (Single precision) //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + +/* + +FPU Operations (fpu_op): +======================== + +0 = add +1 = sub +2 = mul +3 = div +4 = +5 = +6 = +7 = + +Rounding Modes (rmode): +======================= + +0 = round_nearest_even +1 = round_to_zero +2 = round_up +3 = round_down + +*/ + + +module fpu( clk, rmode, fpu_op, opa, opb, out, inf, snan, qnan, ine, overflow, underflow, zero, div_by_zero); +input clk; +input [1:0] rmode; +input [2:0] fpu_op; +input [31:0] opa, opb; +output [31:0] out; +output inf, snan, qnan; +output ine; +output overflow, underflow; +output zero; +output div_by_zero; + +parameter INF = 31'h7f800000, + QNAN = 31'h7fc00001, + SNAN = 31'h7f800001; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires +// +reg zero; +reg [31:0] opa_r, opb_r; // Input operand registers +reg [31:0] out; // Output register +reg div_by_zero; // Divide by zero output register +wire signa, signb; // alias to opX sign +wire sign_fasu; // sign output +wire [26:0] fracta, fractb; // Fraction Outputs from EQU block +wire [7:0] exp_fasu; // Exponent output from EQU block +reg [7:0] exp_r; // Exponent output (registerd) +wire [26:0] fract_out_d; // fraction output +wire co; // carry output +reg [27:0] fract_out_q; // fraction output (registerd) +wire [30:0] out_d; // Intermediate final result output +wire overflow_d, underflow_d;// Overflow/Underflow Indicators +reg overflow, underflow; // Output registers for Overflow & Underflow +reg inf, snan, qnan; // Output Registers for INF, SNAN and QNAN +reg ine; // Output Registers for INE +reg [1:0] rmode_r1, rmode_r2, // Pipeline registers for rounding mode + rmode_r3; +reg [2:0] fpu_op_r1, fpu_op_r2, // Pipeline registers for fp opration + fpu_op_r3; +wire mul_inf, div_inf; +wire mul_00, div_00; + +//////////////////////////////////////////////////////////////////////// +// +// Input Registers +// + +always @(posedge clk) + opa_r <= #1 opa; + +always @(posedge clk) + opb_r <= #1 opb; + +always @(posedge clk) + rmode_r1 <= #1 rmode; + +always @(posedge clk) + rmode_r2 <= #1 rmode_r1; + +always @(posedge clk) + rmode_r3 <= #1 rmode_r2; + +always @(posedge clk) + fpu_op_r1 <= #1 fpu_op; + +always @(posedge clk) + fpu_op_r2 <= #1 fpu_op_r1; + +always @(posedge clk) + fpu_op_r3 <= #1 fpu_op_r2; + +//////////////////////////////////////////////////////////////////////// +// +// Exceptions block +// +wire inf_d, ind_d, qnan_d, snan_d, opa_nan, opb_nan; +wire opa_00, opb_00; +wire opa_inf, opb_inf; +wire opa_dn, opb_dn; + +except u0( .clk(clk), + .opa(opa_r), .opb(opb_r), + .inf(inf_d), .ind(ind_d), + .qnan(qnan_d), .snan(snan_d), + .opa_nan(opa_nan), .opb_nan(opb_nan), + .opa_00(opa_00), .opb_00(opb_00), + .opa_inf(opa_inf), .opb_inf(opb_inf), + .opa_dn(opa_dn), .opb_dn(opb_dn) + ); + +//////////////////////////////////////////////////////////////////////// +// +// Pre-Normalize block +// - Adjusts the numbers to equal exponents and sorts them +// - determine result sign +// - determine actual operation to perform (add or sub) +// + +wire nan_sign_d, result_zero_sign_d; +reg sign_fasu_r; +wire [7:0] exp_mul; +wire sign_mul; +reg sign_mul_r; +wire [23:0] fracta_mul, fractb_mul; +wire inf_mul; +reg inf_mul_r; +wire [1:0] exp_ovf; +reg [1:0] exp_ovf_r; +wire sign_exe; +reg sign_exe_r; +wire [2:0] underflow_fmul_d; + + +pre_norm u1(.clk(clk), // System Clock + .rmode(rmode_r2), // Roundin Mode + .add(!fpu_op_r1[0]), // Add/Sub Input + .opa(opa_r), .opb(opb_r), // Registered OP Inputs + .opa_nan(opa_nan), // OpA is a NAN indicator + .opb_nan(opb_nan), // OpB is a NAN indicator + .fracta_out(fracta), // Equalized and sorted fraction + .fractb_out(fractb), // outputs (Registered) + .exp_dn_out(exp_fasu), // Selected exponent output (registered); + .sign(sign_fasu), // Encoded output Sign (registered) + .nan_sign(nan_sign_d), // Output Sign for NANs (registered) + .result_zero_sign(result_zero_sign_d), // Output Sign for zero result (registered) + .fasu_op(fasu_op) // Actual fasu operation output (registered) + ); + +always @(posedge clk) + sign_fasu_r <= #1 sign_fasu; + +pre_norm_fmul u2( + .clk(clk), + .fpu_op(fpu_op_r1), + .opa(opa_r), .opb(opb_r), + .fracta(fracta_mul), + .fractb(fractb_mul), + .exp_out(exp_mul), // FMUL exponent output (registered) + .sign(sign_mul), // FMUL sign output (registered) + .sign_exe(sign_exe), // FMUL exception sign output (registered) + .inf(inf_mul), // FMUL inf output (registered) + .exp_ovf(exp_ovf), // FMUL exponnent overflow output (registered) + .underflow(underflow_fmul_d) + ); + + +always @(posedge clk) + sign_mul_r <= #1 sign_mul; + +always @(posedge clk) + sign_exe_r <= #1 sign_exe; + +always @(posedge clk) + inf_mul_r <= #1 inf_mul; + +always @(posedge clk) + exp_ovf_r <= #1 exp_ovf; + + +//////////////////////////////////////////////////////////////////////// +// +// Add/Sub +// + +add_sub27 u3( + .add(fasu_op), // Add/Sub + .opa(fracta), // Fraction A input + .opb(fractb), // Fraction B Input + .sum(fract_out_d), // SUM output + .co(co_d) ); // Carry Output + +always @(posedge clk) + fract_out_q <= #1 {co_d, fract_out_d}; + +//////////////////////////////////////////////////////////////////////// +// +// Mul +// +wire [47:0] prod; + +mul_r2 u5(.clk(clk), .opa(fracta_mul), .opb(fractb_mul), .prod(prod)); + +//////////////////////////////////////////////////////////////////////// +// +// Divide +// +wire [49:0] quo; +wire [49:0] fdiv_opa; +wire [49:0] remainder; +wire remainder_00; +reg [4:0] div_opa_ldz_d, div_opa_ldz_r1, div_opa_ldz_r2; + +always @(fracta_mul) + casex(fracta_mul[22:0]) + 23'b1??????????????????????: div_opa_ldz_d = 1; + 23'b01?????????????????????: div_opa_ldz_d = 2; + 23'b001????????????????????: div_opa_ldz_d = 3; + 23'b0001???????????????????: div_opa_ldz_d = 4; + 23'b00001??????????????????: div_opa_ldz_d = 5; + 23'b000001?????????????????: div_opa_ldz_d = 6; + 23'b0000001????????????????: div_opa_ldz_d = 7; + 23'b00000001???????????????: div_opa_ldz_d = 8; + 23'b000000001??????????????: div_opa_ldz_d = 9; + 23'b0000000001?????????????: div_opa_ldz_d = 10; + 23'b00000000001????????????: div_opa_ldz_d = 11; + 23'b000000000001???????????: div_opa_ldz_d = 12; + 23'b0000000000001??????????: div_opa_ldz_d = 13; + 23'b00000000000001?????????: div_opa_ldz_d = 14; + 23'b000000000000001????????: div_opa_ldz_d = 15; + 23'b0000000000000001???????: div_opa_ldz_d = 16; + 23'b00000000000000001??????: div_opa_ldz_d = 17; + 23'b000000000000000001?????: div_opa_ldz_d = 18; + 23'b0000000000000000001????: div_opa_ldz_d = 19; + 23'b00000000000000000001???: div_opa_ldz_d = 20; + 23'b000000000000000000001??: div_opa_ldz_d = 21; + 23'b0000000000000000000001?: div_opa_ldz_d = 22; + 23'b0000000000000000000000?: div_opa_ldz_d = 23; + endcase + +assign fdiv_opa = !(|opa_r[30:23]) ? {(fracta_mul<<div_opa_ldz_d), 26'h0} : {fracta_mul, 26'h0}; + + +div_r2 u6(.clk(clk), .opa(fdiv_opa), .opb(fractb_mul), .quo(quo), .rem(remainder)); + +assign remainder_00 = !(|remainder); + +always @(posedge clk) + div_opa_ldz_r1 <= #1 div_opa_ldz_d; + +always @(posedge clk) + div_opa_ldz_r2 <= #1 div_opa_ldz_r1; + + +//////////////////////////////////////////////////////////////////////// +// +// Normalize Result +// +wire ine_d; +reg [47:0] fract_denorm; +wire [47:0] fract_div; +wire sign_d; +reg sign; +reg [30:0] opa_r1; +reg [47:0] fract_i2f; +reg opas_r1, opas_r2; +wire f2i_out_sign; + +always @(posedge clk) // Exponent must be once cycle delayed + case(fpu_op_r2) + 0,1: exp_r <= #1 exp_fasu; + 2,3: exp_r <= #1 exp_mul; + 4: exp_r <= #1 0; + 5: exp_r <= #1 opa_r1[30:23]; + endcase + +assign fract_div = (opb_dn ? quo[49:2] : {quo[26:0], 21'h0}); + +always @(posedge clk) + opa_r1 <= #1 opa_r[30:0]; + +always @(posedge clk) + fract_i2f <= #1 (fpu_op_r2==5) ? + (sign_d ? 1-{24'h00, (|opa_r1[30:23]), opa_r1[22:0]}-1 : {24'h0, (|opa_r1[30:23]), opa_r1[22:0]}) : + (sign_d ? 1 - {opa_r1, 17'h01} : {opa_r1, 17'h0}); + +always @(fpu_op_r3 or fract_out_q or prod or fract_div or fract_i2f) + case(fpu_op_r3) + 0,1: fract_denorm = {fract_out_q, 20'h0}; + 2: fract_denorm = prod; + 3: fract_denorm = fract_div; + 4,5: fract_denorm = fract_i2f; + endcase + + +always @(posedge clk) + opas_r1 <= #1 opa_r[31]; + +always @(posedge clk) + opas_r2 <= #1 opas_r1; + +assign sign_d = fpu_op_r2[1] ? sign_mul : sign_fasu; + +always @(posedge clk) + sign <= #1 (rmode_r2==2'h3) ? !sign_d : sign_d; + +post_norm u4(.clk(clk), // System Clock + .fpu_op(fpu_op_r3), // Floating Point Operation + .opas(opas_r2), // OPA Sign + .sign(sign), // Sign of the result + .rmode(rmode_r3), // Rounding mode + .fract_in(fract_denorm), // Fraction Input + .exp_ovf(exp_ovf_r), // Exponent Overflow + .exp_in(exp_r), // Exponent Input + .opa_dn(opa_dn), // Operand A Denormalized + .opb_dn(opb_dn), // Operand A Denormalized + .rem_00(remainder_00), // Diveide Remainder is zero + .div_opa_ldz(div_opa_ldz_r2), // Divide opa leading zeros count + .output_zero(mul_00 | div_00), // Force output to Zero + .out(out_d), // Normalized output (un-registered) + .ine(ine_d), // Result Inexact output (un-registered) + .overflow(overflow_d), // Overflow output (un-registered) + .underflow(underflow_d), // Underflow output (un-registered) + .f2i_out_sign(f2i_out_sign) // F2I Output Sign + ); + +//////////////////////////////////////////////////////////////////////// +// +// FPU Outputs +// +reg fasu_op_r1, fasu_op_r2; +wire [30:0] out_fixed; +wire output_zero_fasu; +wire output_zero_fdiv; +wire output_zero_fmul; +reg inf_mul2; +wire overflow_fasu; +wire overflow_fmul; +wire overflow_fdiv; +wire inf_fmul; +wire sign_mul_final; +wire out_d_00; +wire sign_div_final; +wire ine_mul, ine_mula, ine_div, ine_fasu; +wire underflow_fasu, underflow_fmul, underflow_fdiv; +wire underflow_fmul1; +reg [2:0] underflow_fmul_r; +reg opa_nan_r; + + +always @(posedge clk) + fasu_op_r1 <= #1 fasu_op; + +always @(posedge clk) + fasu_op_r2 <= #1 fasu_op_r1; + +always @(posedge clk) + inf_mul2 <= #1 exp_mul == 8'hff; + + +// Force pre-set values for non numerical output +assign mul_inf = (fpu_op_r3==3'b010) & (inf_mul_r | inf_mul2) & (rmode_r3==2'h0); +assign div_inf = (fpu_op_r3==3'b011) & (opb_00 | opa_inf); + +assign mul_00 = (fpu_op_r3==3'b010) & (opa_00 | opb_00); +assign div_00 = (fpu_op_r3==3'b011) & (opa_00 | opb_inf); + +assign out_fixed = ( (qnan_d | snan_d) | + (ind_d & !fasu_op_r2) | + ((fpu_op_r3==3'b011) & opb_00 & opa_00) | + (((opa_inf & opb_00) | (opb_inf & opa_00 )) & fpu_op_r3==3'b010) + ) ? QNAN : INF; + +always @(posedge clk) + out[30:0] <= #1 (mul_inf | div_inf | (inf_d & (fpu_op_r3!=3'b011) & (fpu_op_r3!=3'b101)) | snan_d | qnan_d) & fpu_op_r3!=3'b100 ? out_fixed : + out_d; + +assign out_d_00 = !(|out_d); + +assign sign_mul_final = (sign_exe_r & ((opa_00 & opb_inf) | (opb_00 & opa_inf))) ? !sign_mul_r : sign_mul_r; +assign sign_div_final = (sign_exe_r & (opa_inf & opb_inf)) ? !sign_mul_r : sign_mul_r | (opa_00 & opb_00); + +always @(posedge clk) + out[31] <= #1 ((fpu_op_r3==3'b101) & out_d_00) ? (f2i_out_sign & !(qnan_d | snan_d) ) : + ((fpu_op_r3==3'b010) & !(snan_d | qnan_d)) ? sign_mul_final : + ((fpu_op_r3==3'b011) & !(snan_d | qnan_d)) ? sign_div_final : + (snan_d | qnan_d | ind_d) ? nan_sign_d : + output_zero_fasu ? result_zero_sign_d : + sign_fasu_r; + +// Exception Outputs +assign ine_mula = ((inf_mul_r | inf_mul2 | opa_inf | opb_inf) & (rmode_r3==2'h1) & + !((opa_inf & opb_00) | (opb_inf & opa_00 )) & fpu_op_r3[1]); + +assign ine_mul = (ine_mula | ine_d | inf_fmul | out_d_00 | overflow_d | underflow_d) & + !opa_00 & !opb_00 & !(snan_d | qnan_d | inf_d); +assign ine_div = (ine_d | overflow_d | underflow_d) & !(opb_00 | snan_d | qnan_d | inf_d); +assign ine_fasu = (ine_d | overflow_d | underflow_d) & !(snan_d | qnan_d | inf_d); + +always @(posedge clk) + ine <= #1 fpu_op_r3[2] ? ine_d : + !fpu_op_r3[1] ? ine_fasu : + fpu_op_r3[0] ? ine_div : ine_mul; + + +assign overflow_fasu = overflow_d & !(snan_d | qnan_d | inf_d); +assign overflow_fmul = !inf_d & (inf_mul_r | inf_mul2 | overflow_d) & !(snan_d | qnan_d); +assign overflow_fdiv = (overflow_d & !(opb_00 | inf_d | snan_d | qnan_d)); + +always @(posedge clk) + overflow <= #1 fpu_op_r3[2] ? 0 : + !fpu_op_r3[1] ? overflow_fasu : + fpu_op_r3[0] ? overflow_fdiv : overflow_fmul; + +always @(posedge clk) + underflow_fmul_r <= #1 underflow_fmul_d; + + +assign underflow_fmul1 = underflow_fmul_r[0] | + (underflow_fmul_r[1] & underflow_d ) | + ((opa_dn | opb_dn) & out_d_00 & (prod!=0) & sign) | + (underflow_fmul_r[2] & ((out_d[30:23]==0) | (out_d[22:0]==0))); + +assign underflow_fasu = underflow_d & !(inf_d | snan_d | qnan_d); +assign underflow_fmul = underflow_fmul1 & !(snan_d | qnan_d | inf_mul_r); +assign underflow_fdiv = underflow_fasu & !opb_00; + +always @(posedge clk) + underflow <= #1 fpu_op_r3[2] ? 0 : + !fpu_op_r3[1] ? underflow_fasu : + fpu_op_r3[0] ? underflow_fdiv : underflow_fmul; + +always @(posedge clk) + snan <= #1 snan_d; +/* +wire mul_uf_del; +wire uf2_del, ufb2_del, ufc2_del, underflow_d_del; +wire co_del; +wire [30:0] out_d_del; +wire ov_fasu_del, ov_fmul_del; +wire [2:0] fop; +wire [4:0] ldza_del; +wire [49:0] quo_del; + +delay1 #0 ud000(clk, underflow_fmul1, mul_uf_del); +delay1 #0 ud001(clk, underflow_fmul_r[0], uf2_del); +delay1 #0 ud002(clk, underflow_fmul_r[1], ufb2_del); +delay1 #0 ud003(clk, underflow_d, underflow_d_del); +delay1 #0 ud004(clk, test.u0.u4.exp_out1_co, co_del); +delay1 #0 ud005(clk, underflow_fmul_r[2], ufc2_del); +delay1 #30 ud006(clk, out_d, out_d_del); + +delay1 #0 ud007(clk, overflow_fasu, ov_fasu_del); +delay1 #0 ud008(clk, overflow_fmul, ov_fmul_del); + +delay1 #2 ud009(clk, fpu_op_r3, fop); + +delay3 #4 ud010(clk, div_opa_ldz_d, ldza_del); + +delay1 #49 ud012(clk, quo, quo_del); + +always @(test.error_event) + begin + #0.2 + $display("muf: %b uf0: %b uf1: %b uf2: %b, tx0: %b, co: %b, out_d: %h (%h %h), ov_fasu: %b, ov_fmul: %b, fop: %h", + mul_uf_del, uf2_del, ufb2_del, ufc2_del, underflow_d_del, co_del, out_d_del, out_d_del[30:23], out_d_del[22:0], + ov_fasu_del, ov_fmul_del, fop ); + $display("ldza: %h, quo: %b", + ldza_del, quo_del); + end +*/ + + +// Status Outputs +always @(posedge clk) + qnan <= #1 fpu_op_r3[2] ? 0 : ( + snan_d | qnan_d | (ind_d & !fasu_op_r2) | + (opa_00 & opb_00 & fpu_op_r3==3'b011) | + (((opa_inf & opb_00) | (opb_inf & opa_00 )) & fpu_op_r3==3'b010) + ); + +assign inf_fmul = (((inf_mul_r | inf_mul2) & (rmode_r3==2'h0)) | opa_inf | opb_inf) & + !((opa_inf & opb_00) | (opb_inf & opa_00 )) & + fpu_op_r3==3'b010; + +always @(posedge clk) + inf <= #1 fpu_op_r3[2] ? 0 : + (!(qnan_d | snan_d) & ( + ((&out_d[30:23]) & !(|out_d[22:0]) & !(opb_00 & fpu_op_r3==3'b011)) | + (inf_d & !(ind_d & !fasu_op_r2) & !fpu_op_r3[1]) | + inf_fmul | + (!opa_00 & opb_00 & fpu_op_r3==3'b011) | + (fpu_op_r3==3'b011 & opa_inf & !opb_inf) + ) + ); + +assign output_zero_fasu = out_d_00 & !(inf_d | snan_d | qnan_d); +assign output_zero_fdiv = (div_00 | (out_d_00 & !opb_00)) & !(opa_inf & opb_inf) & + !(opa_00 & opb_00) & !(qnan_d | snan_d); +assign output_zero_fmul = (out_d_00 | opa_00 | opb_00) & + !(inf_mul_r | inf_mul2 | opa_inf | opb_inf | snan_d | qnan_d) & + !(opa_inf & opb_00) & !(opb_inf & opa_00); + +always @(posedge clk) + zero <= #1 fpu_op_r3==3'b101 ? out_d_00 & !(snan_d | qnan_d): + fpu_op_r3==3'b011 ? output_zero_fdiv : + fpu_op_r3==3'b010 ? output_zero_fmul : + output_zero_fasu ; + +always @(posedge clk) + opa_nan_r <= #1 !opa_nan & fpu_op_r2==3'b011; + +always @(posedge clk) + div_by_zero <= #1 opa_nan_r & !opa_00 & !opa_inf & opb_00; + +endmodule
diff --git a/verilog/rtl/fpu/post_norm.v b/verilog/rtl/fpu/post_norm.v new file mode 100644 index 0000000..ff9cf6f --- /dev/null +++ b/verilog/rtl/fpu/post_norm.v
@@ -0,0 +1,676 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Post Norm //// +//// Floating Point Post Normalisation Unit //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + + +`timescale 1ns / 100ps + +module post_norm( clk, fpu_op, opas, sign, rmode, fract_in, exp_in, exp_ovf, + opa_dn, opb_dn, rem_00, div_opa_ldz, output_zero, out, + ine, overflow, underflow, f2i_out_sign); +input clk; +input [2:0] fpu_op; +input opas; +input sign; +input [1:0] rmode; +input [47:0] fract_in; +input [1:0] exp_ovf; +input [7:0] exp_in; +input opa_dn, opb_dn; +input rem_00; +input [4:0] div_opa_ldz; +input output_zero; +output [30:0] out; +output ine; +output overflow, underflow; +output f2i_out_sign; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +wire [22:0] fract_out; +wire [7:0] exp_out; +wire [30:0] out; +wire exp_out1_co, overflow, underflow; +wire [22:0] fract_out_final; +reg [22:0] fract_out_rnd; +wire [8:0] exp_next_mi; +wire dn; +wire exp_rnd_adj; +wire [7:0] exp_out_final; +reg [7:0] exp_out_rnd; +wire op_dn = opa_dn | opb_dn; +wire op_mul = fpu_op[2:0]==3'b010; +wire op_div = fpu_op[2:0]==3'b011; +wire op_i2f = fpu_op[2:0]==3'b100; +wire op_f2i = fpu_op[2:0]==3'b101; +reg [5:0] fi_ldz; + +wire g, r, s; +wire round, round2, round2a, round2_fasu, round2_fmul; +wire [7:0] exp_out_rnd0, exp_out_rnd1, exp_out_rnd2, exp_out_rnd2a; +wire [22:0] fract_out_rnd0, fract_out_rnd1, fract_out_rnd2, fract_out_rnd2a; +wire exp_rnd_adj0, exp_rnd_adj2a; +wire r_sign; +wire ovf0, ovf1; +wire [23:0] fract_out_pl1; +wire [7:0] exp_out_pl1, exp_out_mi1; +wire exp_out_00, exp_out_fe, exp_out_ff, exp_in_00, exp_in_ff; +wire exp_out_final_ff, fract_out_7fffff; +wire [24:0] fract_trunc; +wire [7:0] exp_out1; +wire grs_sel; +wire fract_out_00, fract_in_00; +wire shft_co; +wire [8:0] exp_in_pl1, exp_in_mi1; +wire [47:0] fract_in_shftr; +wire [47:0] fract_in_shftl; + +wire [7:0] exp_div; +wire [7:0] shft2; +wire [7:0] exp_out1_mi1; +wire div_dn; +wire div_nr; +wire grs_sel_div; + +wire div_inf; +wire [6:0] fi_ldz_2a; +wire [7:0] fi_ldz_2; +wire [7:0] div_shft1, div_shft2, div_shft3, div_shft4; +wire div_shft1_co; +wire [8:0] div_exp1; +wire [7:0] div_exp2, div_exp3; +wire left_right, lr_mul, lr_div; +wire [7:0] shift_right, shftr_mul, shftr_div; +wire [7:0] shift_left, shftl_mul, shftl_div; +wire [7:0] fasu_shift; +wire [7:0] exp_fix_div; + +wire [7:0] exp_fix_diva, exp_fix_divb; +wire [5:0] fi_ldz_mi1; +wire [5:0] fi_ldz_mi22; +wire exp_zero; +wire [6:0] ldz_all; +wire [7:0] ldz_dif; + +wire [8:0] div_scht1a; +wire [7:0] f2i_shft; +wire [55:0] exp_f2i_1; +wire f2i_zero, f2i_max; +wire [7:0] f2i_emin; +wire [7:0] conv_shft; +wire [7:0] exp_i2f, exp_f2i, conv_exp; +wire round2_f2i; + +//////////////////////////////////////////////////////////////////////// +// +// Normalize and Round Logic +// + +// --------------------------------------------------------------------- +// Count Leading zeros in fraction + +always @(fract_in) + casex(fract_in) // synopsys full_case parallel_case + 48'b1???????????????????????????????????????????????: fi_ldz = 1; + 48'b01??????????????????????????????????????????????: fi_ldz = 2; + 48'b001?????????????????????????????????????????????: fi_ldz = 3; + 48'b0001????????????????????????????????????????????: fi_ldz = 4; + 48'b00001???????????????????????????????????????????: fi_ldz = 5; + 48'b000001??????????????????????????????????????????: fi_ldz = 6; + 48'b0000001?????????????????????????????????????????: fi_ldz = 7; + 48'b00000001????????????????????????????????????????: fi_ldz = 8; + 48'b000000001???????????????????????????????????????: fi_ldz = 9; + 48'b0000000001??????????????????????????????????????: fi_ldz = 10; + 48'b00000000001?????????????????????????????????????: fi_ldz = 11; + 48'b000000000001????????????????????????????????????: fi_ldz = 12; + 48'b0000000000001???????????????????????????????????: fi_ldz = 13; + 48'b00000000000001??????????????????????????????????: fi_ldz = 14; + 48'b000000000000001?????????????????????????????????: fi_ldz = 15; + 48'b0000000000000001????????????????????????????????: fi_ldz = 16; + 48'b00000000000000001???????????????????????????????: fi_ldz = 17; + 48'b000000000000000001??????????????????????????????: fi_ldz = 18; + 48'b0000000000000000001?????????????????????????????: fi_ldz = 19; + 48'b00000000000000000001????????????????????????????: fi_ldz = 20; + 48'b000000000000000000001???????????????????????????: fi_ldz = 21; + 48'b0000000000000000000001??????????????????????????: fi_ldz = 22; + 48'b00000000000000000000001?????????????????????????: fi_ldz = 23; + 48'b000000000000000000000001????????????????????????: fi_ldz = 24; + 48'b0000000000000000000000001???????????????????????: fi_ldz = 25; + 48'b00000000000000000000000001??????????????????????: fi_ldz = 26; + 48'b000000000000000000000000001?????????????????????: fi_ldz = 27; + 48'b0000000000000000000000000001????????????????????: fi_ldz = 28; + 48'b00000000000000000000000000001???????????????????: fi_ldz = 29; + 48'b000000000000000000000000000001??????????????????: fi_ldz = 30; + 48'b0000000000000000000000000000001?????????????????: fi_ldz = 31; + 48'b00000000000000000000000000000001????????????????: fi_ldz = 32; + 48'b000000000000000000000000000000001???????????????: fi_ldz = 33; + 48'b0000000000000000000000000000000001??????????????: fi_ldz = 34; + 48'b00000000000000000000000000000000001?????????????: fi_ldz = 35; + 48'b000000000000000000000000000000000001????????????: fi_ldz = 36; + 48'b0000000000000000000000000000000000001???????????: fi_ldz = 37; + 48'b00000000000000000000000000000000000001??????????: fi_ldz = 38; + 48'b000000000000000000000000000000000000001?????????: fi_ldz = 39; + 48'b0000000000000000000000000000000000000001????????: fi_ldz = 40; + 48'b00000000000000000000000000000000000000001???????: fi_ldz = 41; + 48'b000000000000000000000000000000000000000001??????: fi_ldz = 42; + 48'b0000000000000000000000000000000000000000001?????: fi_ldz = 43; + 48'b00000000000000000000000000000000000000000001????: fi_ldz = 44; + 48'b000000000000000000000000000000000000000000001???: fi_ldz = 45; + 48'b0000000000000000000000000000000000000000000001??: fi_ldz = 46; + 48'b00000000000000000000000000000000000000000000001?: fi_ldz = 47; + 48'b00000000000000000000000000000000000000000000000?: fi_ldz = 48; + endcase + + +// --------------------------------------------------------------------- +// Normalize + +wire exp_in_80; +wire rmode_00, rmode_01, rmode_10, rmode_11; + +// Misc common signals +assign exp_in_ff = &exp_in; +assign exp_in_00 = !(|exp_in); +assign exp_in_80 = exp_in[7] & !(|exp_in[6:0]); +assign exp_out_ff = &exp_out; +assign exp_out_00 = !(|exp_out); +assign exp_out_fe = &exp_out[7:1] & !exp_out[0]; +assign exp_out_final_ff = &exp_out_final; + +assign fract_out_7fffff = &fract_out; +assign fract_out_00 = !(|fract_out); +assign fract_in_00 = !(|fract_in); + +assign rmode_00 = (rmode==2'b00); +assign rmode_01 = (rmode==2'b01); +assign rmode_10 = (rmode==2'b10); +assign rmode_11 = (rmode==2'b11); + +// Fasu Output will be denormalized ... +assign dn = !op_mul & !op_div & (exp_in_00 | (exp_next_mi[8] & !fract_in[47]) ); + +// --------------------------------------------------------------------- +// Fraction Normalization +parameter f2i_emax = 8'h9d; + +// Incremented fraction for rounding +assign fract_out_pl1 = fract_out + 1; + +// Special Signals for f2i +assign f2i_emin = rmode_00 ? 8'h7e : 8'h7f; +assign f2i_zero = (!opas & (exp_in<f2i_emin)) | (opas & (exp_in>f2i_emax)) | (opas & (exp_in<f2i_emin) & (fract_in_00 | !rmode_11)); +assign f2i_max = (!opas & (exp_in>f2i_emax)) | (opas & (exp_in<f2i_emin) & !fract_in_00 & rmode_11); + +// Claculate various shifting options + +assign {shft_co,shftr_mul} = (!exp_ovf[1] & exp_in_00) ? {1'b0, exp_out} : exp_in_mi1 ; +assign {div_shft1_co, div_shft1} = exp_in_00 ? {1'b0, div_opa_ldz} : div_scht1a; + +assign div_scht1a = exp_in-div_opa_ldz; // 9 bits - includes carry out +assign div_shft2 = exp_in+2; +assign div_shft3 = div_opa_ldz+exp_in; +assign div_shft4 = div_opa_ldz-exp_in; + +assign div_dn = op_dn & div_shft1_co; +assign div_nr = op_dn & exp_ovf[1] & !(|fract_in[46:23]) & (div_shft3>8'h16); + +assign f2i_shft = exp_in-8'h7d; + +// Select shifting direction +assign left_right = op_div ? lr_div : op_mul ? lr_mul : 1; + +assign lr_div = (op_dn & !exp_ovf[1] & exp_ovf[0]) ? 1 : + (op_dn & exp_ovf[1]) ? 0 : + (op_dn & div_shft1_co) ? 0 : + (op_dn & exp_out_00) ? 1 : + (!op_dn & exp_out_00 & !exp_ovf[1]) ? 1 : + exp_ovf[1] ? 0 : + 1; +assign lr_mul = (shft_co | (!exp_ovf[1] & exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00) )) ? 1 : + ( exp_ovf[1] | exp_in_00 ) ? 0 : + 1; + +// Select Left and Right shift value +assign fasu_shift = (dn | exp_out_00) ? (exp_in_00 ? 8'h2 : exp_in_pl1[7:0]) : {2'h0, fi_ldz}; +assign shift_right = op_div ? shftr_div : shftr_mul; + +assign conv_shft = op_f2i ? f2i_shft : {2'h0, fi_ldz}; + +assign shift_left = op_div ? shftl_div : op_mul ? shftl_mul : (op_f2i | op_i2f) ? conv_shft : fasu_shift; + +assign shftl_mul = (shft_co | + (!exp_ovf[1] & exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00))) ? exp_in_pl1[7:0] : {2'h0, fi_ldz}; + +assign shftl_div = ( op_dn & exp_out_00 & !(!exp_ovf[1] & exp_ovf[0])) ? div_shft1[7:0] : + (!op_dn & exp_out_00 & !exp_ovf[1]) ? exp_in[7:0] : + {2'h0, fi_ldz}; +assign shftr_div = (op_dn & exp_ovf[1]) ? div_shft3 : + (op_dn & div_shft1_co) ? div_shft4 : + div_shft2; +// Do the actual shifting +assign fract_in_shftr = (|shift_right[7:6]) ? 0 : fract_in>>shift_right[5:0]; +assign fract_in_shftl = (|shift_left[7:6] | (f2i_zero & op_f2i)) ? 0 : fract_in<<shift_left[5:0]; + +// Chose final fraction output +assign {fract_out,fract_trunc} = left_right ? fract_in_shftl : fract_in_shftr; + +// --------------------------------------------------------------------- +// Exponent Normalization + +assign fi_ldz_mi1 = fi_ldz - 1; +assign fi_ldz_mi22 = fi_ldz - 22; +assign exp_out_pl1 = exp_out + 1; +assign exp_out_mi1 = exp_out - 1; +assign exp_in_pl1 = exp_in + 1; // 9 bits - includes carry out +assign exp_in_mi1 = exp_in - 1; // 9 bits - includes carry out +assign exp_out1_mi1 = exp_out1 - 1; + +assign exp_next_mi = exp_in_pl1 - fi_ldz_mi1; // 9 bits - includes carry out + +assign exp_fix_diva = exp_in - fi_ldz_mi22; +assign exp_fix_divb = exp_in - fi_ldz_mi1; + +assign exp_zero = (exp_ovf[1] & !exp_ovf[0] & op_mul & (!exp_rnd_adj2a | !rmode[1])) | (op_mul & exp_out1_co); +assign {exp_out1_co, exp_out1} = fract_in[47] ? exp_in_pl1 : exp_next_mi; + +assign f2i_out_sign = !opas ? ((exp_in<f2i_emin) ? 0 : (exp_in>f2i_emax) ? 0 : opas) : + ((exp_in<f2i_emin) ? 0 : (exp_in>f2i_emax) ? 1 : opas); + +assign exp_i2f = fract_in_00 ? (opas ? 8'h9e : 0) : (8'h9e-fi_ldz); +assign exp_f2i_1 = {{8{fract_in[47]}}, fract_in }<<f2i_shft; +assign exp_f2i = f2i_zero ? 0 : f2i_max ? 8'hff : exp_f2i_1[55:48]; +assign conv_exp = op_f2i ? exp_f2i : exp_i2f; + +assign exp_out = op_div ? exp_div : (op_f2i | op_i2f) ? conv_exp : exp_zero ? 8'h0 : dn ? {6'h0, fract_in[47:46]} : exp_out1; + +assign ldz_all = div_opa_ldz + fi_ldz; +assign ldz_dif = fi_ldz_2 - div_opa_ldz; +assign fi_ldz_2a = 6'd23 - fi_ldz; +assign fi_ldz_2 = {fi_ldz_2a[6], fi_ldz_2a[6:0]}; + +assign div_exp1 = exp_in_mi1 + fi_ldz_2; // 9 bits - includes carry out + +assign div_exp2 = exp_in_pl1 - ldz_all; +assign div_exp3 = exp_in + ldz_dif; + +assign exp_div =(opa_dn & opb_dn) ? div_exp3 : + opb_dn ? div_exp1[7:0] : + (opa_dn & !( (exp_in<div_opa_ldz) | (div_exp2>9'hfe) )) ? div_exp2 : + (opa_dn | (exp_in_00 & !exp_ovf[1]) ) ? 0 : + exp_out1_mi1; + +assign div_inf = opb_dn & !opa_dn & (div_exp1[7:0] < 8'h7f); + +// --------------------------------------------------------------------- +// Round + +// Extract rounding (GRS) bits +assign grs_sel_div = op_div & (exp_ovf[1] | div_dn | exp_out1_co | exp_out_00); + +assign g = grs_sel_div ? fract_out[0] : fract_out[0]; +assign r = grs_sel_div ? (fract_trunc[24] & !div_nr) : fract_trunc[24]; +assign s = grs_sel_div ? |fract_trunc[24:0] : (|fract_trunc[23:0] | (fract_trunc[24] & op_div)); + +// Round to nearest even +assign round = (g & r) | (r & s) ; +assign {exp_rnd_adj0, fract_out_rnd0} = round ? fract_out_pl1 : {1'b0, fract_out}; +assign exp_out_rnd0 = exp_rnd_adj0 ? exp_out_pl1 : exp_out; +assign ovf0 = exp_out_final_ff & !rmode_01 & !op_f2i; + +// round to zero +assign fract_out_rnd1 = (exp_out_ff & !op_div & !dn & !op_f2i) ? 23'h7fffff : fract_out; +assign exp_fix_div = (fi_ldz>22) ? exp_fix_diva : exp_fix_divb; +assign exp_out_rnd1 = (g & r & s & exp_in_ff) ? (op_div ? exp_fix_div : exp_next_mi[7:0]) : + (exp_out_ff & !op_f2i) ? exp_in : exp_out; +assign ovf1 = exp_out_ff & !dn; + +// round to +inf (UP) and -inf (DOWN) +assign r_sign = sign; + +assign round2a = !exp_out_fe | !fract_out_7fffff | (exp_out_fe & fract_out_7fffff); +assign round2_fasu = ((r | s) & !r_sign) & (!exp_out[7] | (exp_out[7] & round2a)); + +assign round2_fmul = !r_sign & + ( + (exp_ovf[1] & !fract_in_00 & + ( ((!exp_out1_co | op_dn) & (r | s | (!rem_00 & op_div) )) | fract_out_00 | (!op_dn & !op_div)) + ) | + ( + (r | s | (!rem_00 & op_div)) & ( + (!exp_ovf[1] & (exp_in_80 | !exp_ovf[0])) | op_div | + ( exp_ovf[1] & !exp_ovf[0] & exp_out1_co) + ) + ) + ); + +assign round2_f2i = rmode_10 & (( |fract_in[23:0] & !opas & (exp_in<8'h80 )) | (|fract_trunc)); +assign round2 = (op_mul | op_div) ? round2_fmul : op_f2i ? round2_f2i : round2_fasu; + +assign {exp_rnd_adj2a, fract_out_rnd2a} = round2 ? fract_out_pl1 : {1'b0, fract_out}; +assign exp_out_rnd2a = exp_rnd_adj2a ? ((exp_ovf[1] & op_mul) ? exp_out_mi1 : exp_out_pl1) : exp_out; + +assign fract_out_rnd2 = (r_sign & exp_out_ff & !op_div & !dn & !op_f2i) ? 23'h7fffff : fract_out_rnd2a; +assign exp_out_rnd2 = (r_sign & exp_out_ff & !op_f2i) ? 8'hfe : exp_out_rnd2a; + + +// Choose rounding mode +always @(rmode or exp_out_rnd0 or exp_out_rnd1 or exp_out_rnd2) + case(rmode) // synopsys full_case parallel_case + 0: exp_out_rnd = exp_out_rnd0; + 1: exp_out_rnd = exp_out_rnd1; + 2,3: exp_out_rnd = exp_out_rnd2; + endcase + +always @(rmode or fract_out_rnd0 or fract_out_rnd1 or fract_out_rnd2) + case(rmode) // synopsys full_case parallel_case + 0: fract_out_rnd = fract_out_rnd0; + 1: fract_out_rnd = fract_out_rnd1; + 2,3: fract_out_rnd = fract_out_rnd2; + endcase + +// --------------------------------------------------------------------- +// Final Output Mux +// Fix Output for denormalized and special numbers +wire max_num, inf_out; + +assign max_num = ( !rmode_00 & (op_mul | op_div ) & ( + ( exp_ovf[1] & exp_ovf[0]) | + (!exp_ovf[1] & !exp_ovf[0] & exp_in_ff & (fi_ldz_2<24) & (exp_out!=8'hfe) ) + ) + ) | + + ( op_div & ( + ( rmode_01 & ( div_inf | + (exp_out_ff & !exp_ovf[1] ) | + (exp_ovf[1] & exp_ovf[0] ) + ) + ) | + + ( rmode[1] & !exp_ovf[1] & ( + ( exp_ovf[0] & exp_in_ff & r_sign & fract_in[47] + ) | + + ( r_sign & ( + (fract_in[47] & div_inf) | + (exp_in[7] & !exp_out_rnd[7] & !exp_in_80 & exp_out!=8'h7f ) | + (exp_in[7] & exp_out_rnd[7] & r_sign & exp_out_ff & op_dn & + div_exp1>9'h0fe ) + ) + ) | + + ( exp_in_00 & r_sign & ( + div_inf | + (r_sign & exp_out_ff & fi_ldz_2<24) + ) + ) + ) + ) + ) + ); + + +assign inf_out = (rmode[1] & (op_mul | op_div) & !r_sign & ( (exp_in_ff & !op_div) | + (exp_ovf[1] & exp_ovf[0] & (exp_in_00 | exp_in[7]) ) + ) + ) | (div_inf & op_div & ( + rmode_00 | + (rmode[1] & !exp_in_ff & !exp_ovf[1] & !exp_ovf[0] & !r_sign ) | + (rmode[1] & !exp_ovf[1] & exp_ovf[0] & exp_in_00 & !r_sign) + ) + ) | (op_div & rmode[1] & exp_in_ff & op_dn & !r_sign & (fi_ldz_2 < 24) & (exp_out_rnd!=8'hfe) ); + +assign fract_out_final = (inf_out | ovf0 | output_zero ) ? 23'h0 : + (max_num | (f2i_max & op_f2i) ) ? 23'h7fffff : + fract_out_rnd; + +assign exp_out_final = ((op_div & exp_ovf[1] & !exp_ovf[0]) | output_zero ) ? 8'h00 : + ((op_div & exp_ovf[1] & exp_ovf[0] & rmode_00) | inf_out | (f2i_max & op_f2i) ) ? 8'hff : + max_num ? 8'hfe : + exp_out_rnd; + + +// --------------------------------------------------------------------- +// Pack Result + +assign out = {exp_out_final, fract_out_final}; + +// --------------------------------------------------------------------- +// Exceptions +wire underflow_fmul; +wire overflow_fdiv; +wire undeflow_div; + +wire z = shft_co | ( exp_ovf[1] | exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00)); + +assign underflow_fmul = ( (|fract_trunc) & z & !exp_in_ff ) | + (fract_out_00 & !fract_in_00 & exp_ovf[1]); + +assign undeflow_div = !(exp_ovf[1] & exp_ovf[0] & rmode_00) & !inf_out & !max_num & exp_out_final!=8'hff & ( + + ((|fract_trunc) & !opb_dn & ( + ( op_dn & !exp_ovf[1] & exp_ovf[0]) | + ( op_dn & exp_ovf[1]) | + ( op_dn & div_shft1_co) | + exp_out_00 | + exp_ovf[1] + ) + + ) | + + ( exp_ovf[1] & !exp_ovf[0] & ( + ( op_dn & exp_in>8'h16 & fi_ldz<23) | + ( op_dn & exp_in<23 & fi_ldz<23 & !rem_00) | + ( !op_dn & (exp_in[7]==exp_div[7]) & !rem_00) | + ( !op_dn & exp_in_00 & (exp_div[7:1]==7'h7f) ) | + ( !op_dn & exp_in<8'h7f & exp_in>8'h20 ) + ) + ) | + + (!exp_ovf[1] & !exp_ovf[0] & ( + ( op_dn & fi_ldz<23 & exp_out_00) | + ( exp_in_00 & !rem_00) | + ( !op_dn & ldz_all<23 & exp_in==1 & exp_out_00 & !rem_00) + ) + ) + + ); + +assign underflow = op_div ? undeflow_div : op_mul ? underflow_fmul : (!fract_in[47] & exp_out1_co) & !dn; + +assign overflow_fdiv = inf_out | + (!rmode_00 & max_num) | + (exp_in[7] & op_dn & exp_out_ff) | + (exp_ovf[0] & (exp_ovf[1] | exp_out_ff) ); + +assign overflow = op_div ? overflow_fdiv : (ovf0 | ovf1); + +wire f2i_ine; + +assign f2i_ine = (f2i_zero & !fract_in_00 & !opas) | + (|fract_trunc) | + (f2i_zero & (exp_in<8'h80) & opas & !fract_in_00) | + (f2i_max & rmode_11 & (exp_in<8'h80)); + + + +assign ine = op_f2i ? f2i_ine : + op_i2f ? (|fract_trunc) : + ((r & !dn) | (s & !dn) | max_num | (op_div & !rem_00)); + +// --------------------------------------------------------------------- +// Debugging Stuff + +// synopsys translate_off + +wire [26:0] fracta_del, fractb_del; +wire [2:0] grs_del; +wire dn_del; +wire [7:0] exp_in_del; +wire [7:0] exp_out_del; +wire [22:0] fract_out_del; +wire [47:0] fract_in_del; +wire overflow_del; +wire [1:0] exp_ovf_del; +wire [22:0] fract_out_x_del, fract_out_rnd2a_del; +wire [24:0] trunc_xx_del; +wire exp_rnd_adj2a_del; +wire [22:0] fract_dn_del; +wire [4:0] div_opa_ldz_del; +wire [23:0] fracta_div_del; +wire [23:0] fractb_div_del; +wire div_inf_del; +wire [7:0] fi_ldz_2_del; +wire inf_out_del, max_out_del; +wire [5:0] fi_ldz_del; +wire rx_del; +wire ez_del; +wire lr; +wire [7:0] shr, shl, exp_div_del; + +delay2 #26 ud000(clk, test.u0.fracta, fracta_del); +delay2 #26 ud001(clk, test.u0.fractb, fractb_del); +delay1 #2 ud002(clk, {g,r,s}, grs_del); +delay1 #0 ud004(clk, dn, dn_del); +delay1 #7 ud005(clk, exp_in, exp_in_del); +delay1 #7 ud007(clk, exp_out_rnd, exp_out_del); +delay1 #47 ud009(clk, fract_in, fract_in_del); +delay1 #0 ud010(clk, overflow, overflow_del); +delay1 #1 ud011(clk, exp_ovf, exp_ovf_del); +delay1 #22 ud014(clk, fract_out, fract_out_x_del); +delay1 #24 ud015(clk, fract_trunc, trunc_xx_del); +delay1 #0 ud017(clk, exp_rnd_adj2a, exp_rnd_adj2a_del); +delay1 #4 ud019(clk, div_opa_ldz, div_opa_ldz_del); +delay3 #23 ud020(clk, test.u0.fdiv_opa[49:26], fracta_div_del); +delay3 #23 ud021(clk, test.u0.fractb_mul, fractb_div_del); +delay1 #0 ud023(clk, div_inf, div_inf_del); +delay1 #7 ud024(clk, fi_ldz_2, fi_ldz_2_del); +delay1 #0 ud025(clk, inf_out, inf_out_del); +delay1 #0 ud026(clk, max_num, max_num_del); +delay1 #5 ud027(clk, fi_ldz, fi_ldz_del); +delay1 #0 ud028(clk, rem_00, rx_del); + +delay1 #0 ud029(clk, left_right, lr); +delay1 #7 ud030(clk, shift_right, shr); +delay1 #7 ud031(clk, shift_left, shl); +delay1 #22 ud032(clk, fract_out_rnd2a, fract_out_rnd2a_del); + +delay1 #7 ud033(clk, exp_div, exp_div_del); + +always @(test.error_event) + begin + + $display("\n----------------------------------------------"); + + $display("ERROR: GRS: %b exp_ovf: %b dn: %h exp_in: %h exp_out: %h, exp_rnd_adj2a: %b", + grs_del, exp_ovf_del, dn_del, exp_in_del, exp_out_del, exp_rnd_adj2a_del); + + $display(" div_opa: %b, div_opb: %b, rem_00: %b, exp_div: %h", + fracta_div_del, fractb_div_del, rx_del, exp_div_del); + + $display(" lr: %b, shl: %h, shr: %h", + lr, shl, shr); + + + $display(" overflow: %b, fract_in=%b fa:%h fb:%h", + overflow_del, fract_in_del, fracta_del, fractb_del); + + $display(" div_opa_ldz: %h, div_inf: %b, inf_out: %b, max_num: %b, fi_ldz: %h, fi_ldz_2: %h", + div_opa_ldz_del, div_inf_del, inf_out_del, max_num_del, fi_ldz_del, fi_ldz_2_del); + + $display(" fract_out_x: %b, fract_out_rnd2a_del: %h, fract_trunc: %b\n", + fract_out_x_del, fract_out_rnd2a_del, trunc_xx_del); + end + + +// synopsys translate_on + +endmodule + +// synopsys translate_off + +module delay1(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out; + +always @(posedge clk) + out <= #1 in; + +endmodule + + +module delay2(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out, r1; + +always @(posedge clk) + r1 <= #1 in; + +always @(posedge clk) + out <= #1 r1; + +endmodule + +module delay3(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out, r1, r2; + +always @(posedge clk) + r1 <= #1 in; + +always @(posedge clk) + r2 <= #1 r1; + +always @(posedge clk) + out <= #1 r2; + +endmodule + +// synopsys translate_on \ No newline at end of file
diff --git a/verilog/rtl/fpu/pre_norm.v b/verilog/rtl/fpu/pre_norm.v new file mode 100644 index 0000000..c54c71f --- /dev/null +++ b/verilog/rtl/fpu/pre_norm.v
@@ -0,0 +1,270 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Pre Normalize //// +//// Pre Normalization Unit for Add/Sub Operations //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + + +module pre_norm(clk, rmode, add, opa, opb, opa_nan, opb_nan, fracta_out, + fractb_out, exp_dn_out, sign, nan_sign, result_zero_sign, + fasu_op); +input clk; +input [1:0] rmode; +input add; +input [31:0] opa, opb; +input opa_nan, opb_nan; +output [26:0] fracta_out, fractb_out; +output [7:0] exp_dn_out; +output sign; +output nan_sign, result_zero_sign; +output fasu_op; // Operation Output + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +wire signa, signb; // alias to opX sign +wire [7:0] expa, expb; // alias to opX exponent +wire [22:0] fracta, fractb; // alias to opX fraction +wire expa_lt_expb; // expa is larger than expb indicator +wire fractb_lt_fracta; // fractb is larger than fracta indicator +reg [7:0] exp_dn_out; // de normalized exponent output +wire [7:0] exp_small, exp_large; +wire [7:0] exp_diff; // Numeric difference of the two exponents +wire [22:0] adj_op; // Fraction adjustment: input +wire [26:0] adj_op_tmp; +wire [26:0] adj_op_out; // Fraction adjustment: output +wire [26:0] fracta_n, fractb_n; // Fraction selection after normalizing +wire [26:0] fracta_s, fractb_s; // Fraction Sorting out +reg [26:0] fracta_out, fractb_out; // Fraction Output +reg sign, sign_d; // Sign Output +reg add_d; // operation (add/sub) +reg fasu_op; // operation (add/sub) register +wire expa_dn, expb_dn; +reg sticky; +reg result_zero_sign; +reg add_r, signa_r, signb_r; +wire [4:0] exp_diff_sft; +wire exp_lt_27; +wire op_dn; +wire [26:0] adj_op_out_sft; +reg fracta_lt_fractb, fracta_eq_fractb; +wire nan_sign1; +reg nan_sign; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign signa = opa[31]; +assign signb = opb[31]; +assign expa = opa[30:23]; +assign expb = opb[30:23]; +assign fracta = opa[22:0]; +assign fractb = opb[22:0]; + +//////////////////////////////////////////////////////////////////////// +// +// Pre-Normalize exponents (and fractions) +// + +assign expa_lt_expb = expa > expb; // expa is larger than expb + +// --------------------------------------------------------------------- +// Normalize + +assign expa_dn = !(|expa); // opa denormalized +assign expb_dn = !(|expb); // opb denormalized + +// --------------------------------------------------------------------- +// Calculate the difference between the smaller and larger exponent + +wire [7:0] exp_diff1, exp_diff1a, exp_diff2; + +assign exp_small = expa_lt_expb ? expb : expa; +assign exp_large = expa_lt_expb ? expa : expb; +assign exp_diff1 = exp_large - exp_small; +assign exp_diff1a = exp_diff1-1; +assign exp_diff2 = (expa_dn | expb_dn) ? exp_diff1a : exp_diff1; +assign exp_diff = (expa_dn & expb_dn) ? 8'h0 : exp_diff2; + +always @(posedge clk) // If numbers are equal we should return zero + exp_dn_out <= #1 (!add_d & expa==expb & fracta==fractb) ? 8'h0 : exp_large; + +// --------------------------------------------------------------------- +// Adjust the smaller fraction + + +assign op_dn = expa_lt_expb ? expb_dn : expa_dn; +assign adj_op = expa_lt_expb ? fractb : fracta; +assign adj_op_tmp = { ~op_dn, adj_op, 3'b0 }; // recover hidden bit (op_dn) + +// adj_op_out is 27 bits wide, so can only be shifted 27 bits to the right +assign exp_lt_27 = exp_diff > 8'd27; +assign exp_diff_sft = exp_lt_27 ? 5'd27 : exp_diff[4:0]; +assign adj_op_out_sft = adj_op_tmp >> exp_diff_sft; +assign adj_op_out = {adj_op_out_sft[26:1], adj_op_out_sft[0] | sticky }; + +// --------------------------------------------------------------------- +// Get truncated portion (sticky bit) + +always @(exp_diff_sft or adj_op_tmp) + case(exp_diff_sft) // synopsys full_case parallel_case + 00: sticky = 1'h0; + 01: sticky = adj_op_tmp[0]; + 02: sticky = |adj_op_tmp[01:0]; + 03: sticky = |adj_op_tmp[02:0]; + 04: sticky = |adj_op_tmp[03:0]; + 05: sticky = |adj_op_tmp[04:0]; + 06: sticky = |adj_op_tmp[05:0]; + 07: sticky = |adj_op_tmp[06:0]; + 08: sticky = |adj_op_tmp[07:0]; + 09: sticky = |adj_op_tmp[08:0]; + 10: sticky = |adj_op_tmp[09:0]; + 11: sticky = |adj_op_tmp[10:0]; + 12: sticky = |adj_op_tmp[11:0]; + 13: sticky = |adj_op_tmp[12:0]; + 14: sticky = |adj_op_tmp[13:0]; + 15: sticky = |adj_op_tmp[14:0]; + 16: sticky = |adj_op_tmp[15:0]; + 17: sticky = |adj_op_tmp[16:0]; + 18: sticky = |adj_op_tmp[17:0]; + 19: sticky = |adj_op_tmp[18:0]; + 20: sticky = |adj_op_tmp[19:0]; + 21: sticky = |adj_op_tmp[20:0]; + 22: sticky = |adj_op_tmp[21:0]; + 23: sticky = |adj_op_tmp[22:0]; + 24: sticky = |adj_op_tmp[23:0]; + 25: sticky = |adj_op_tmp[24:0]; + 26: sticky = |adj_op_tmp[25:0]; + 27: sticky = |adj_op_tmp[26:0]; + endcase + +// --------------------------------------------------------------------- +// Select operands for add/sub (recover hidden bit) + +assign fracta_n = expa_lt_expb ? {~expa_dn, fracta, 3'b0} : adj_op_out; +assign fractb_n = expa_lt_expb ? adj_op_out : {~expb_dn, fractb, 3'b0}; + +// --------------------------------------------------------------------- +// Sort operands (for sub only) + +assign fractb_lt_fracta = fractb_n > fracta_n; // fractb is larger than fracta +assign fracta_s = fractb_lt_fracta ? fractb_n : fracta_n; +assign fractb_s = fractb_lt_fracta ? fracta_n : fractb_n; + +always @(posedge clk) + fracta_out <= #1 fracta_s; + +always @(posedge clk) + fractb_out <= #1 fractb_s; + +// --------------------------------------------------------------------- +// Determine sign for the output + +// sign: 0=Positive Number; 1=Negative Number +always @(signa or signb or add or fractb_lt_fracta) + case({signa, signb, add}) // synopsys full_case parallel_case + + // Add + 3'b0_0_1: sign_d = 0; + 3'b0_1_1: sign_d = fractb_lt_fracta; + 3'b1_0_1: sign_d = !fractb_lt_fracta; + 3'b1_1_1: sign_d = 1; + + // Sub + 3'b0_0_0: sign_d = fractb_lt_fracta; + 3'b0_1_0: sign_d = 0; + 3'b1_0_0: sign_d = 1; + 3'b1_1_0: sign_d = !fractb_lt_fracta; + endcase + +always @(posedge clk) + sign <= #1 sign_d; + +// Fix sign for ZERO result +always @(posedge clk) + signa_r <= #1 signa; + +always @(posedge clk) + signb_r <= #1 signb; + +always @(posedge clk) + add_r <= #1 add; + +always @(posedge clk) + result_zero_sign <= #1 ( add_r & signa_r & signb_r) | + (!add_r & signa_r & !signb_r) | + ( add_r & (signa_r | signb_r) & (rmode==3)) | + (!add_r & (signa_r == signb_r) & (rmode==3)); + +// Fix sign for NAN result +always @(posedge clk) + fracta_lt_fractb <= #1 fracta < fractb; + +always @(posedge clk) + fracta_eq_fractb <= #1 fracta == fractb; + +assign nan_sign1 = fracta_eq_fractb ? (signa_r & signb_r) : fracta_lt_fractb ? signb_r : signa_r; + +always @(posedge clk) + nan_sign <= #1 (opa_nan & opb_nan) ? nan_sign1 : opb_nan ? signb_r : signa_r; + +//////////////////////////////////////////////////////////////////////// +// +// Decode Add/Sub operation +// + +// add: 1=Add; 0=Subtract +always @(signa or signb or add) + case({signa, signb, add}) // synopsys full_case parallel_case + + // Add + 3'b0_0_1: add_d = 1; + 3'b0_1_1: add_d = 0; + 3'b1_0_1: add_d = 0; + 3'b1_1_1: add_d = 1; + + // Sub + 3'b0_0_0: add_d = 0; + 3'b0_1_0: add_d = 1; + 3'b1_0_0: add_d = 1; + 3'b1_1_0: add_d = 0; + endcase + +always @(posedge clk) + fasu_op <= #1 add_d; + +endmodule
diff --git a/verilog/rtl/fpu/pre_norm_fmul.v b/verilog/rtl/fpu/pre_norm_fmul.v new file mode 100644 index 0000000..26ddfeb --- /dev/null +++ b/verilog/rtl/fpu/pre_norm_fmul.v
@@ -0,0 +1,150 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Pre Normalize //// +//// Floating Point Pre Normalization Unit for FMUL //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + +module pre_norm_fmul(clk, fpu_op, opa, opb, fracta, fractb, exp_out, sign, + sign_exe, inf, exp_ovf, underflow); +input clk; +input [2:0] fpu_op; +input [31:0] opa, opb; +output [23:0] fracta, fractb; +output [7:0] exp_out; +output sign, sign_exe; +output inf; +output [1:0] exp_ovf; +output [2:0] underflow; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +reg [7:0] exp_out; +wire signa, signb; +reg sign, sign_d; +reg sign_exe; +reg inf; +wire [1:0] exp_ovf_d; +reg [1:0] exp_ovf; +wire [7:0] expa, expb; +wire [7:0] exp_tmp1, exp_tmp2; +wire co1, co2; +wire expa_dn, expb_dn; +wire [7:0] exp_out_a; +wire opa_00, opb_00, fracta_00, fractb_00; +wire [7:0] exp_tmp3, exp_tmp4, exp_tmp5; +wire [2:0] underflow_d; +reg [2:0] underflow; +wire op_div = (fpu_op == 3'b011); +wire [7:0] exp_out_mul, exp_out_div; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign signa = opa[31]; +assign signb = opb[31]; +assign expa = opa[30:23]; +assign expb = opb[30:23]; + +//////////////////////////////////////////////////////////////////////// +// +// Calculate Exponenet +// + +assign expa_dn = !(|expa); +assign expb_dn = !(|expb); +assign opa_00 = !(|opa[30:0]); +assign opb_00 = !(|opb[30:0]); +assign fracta_00 = !(|opa[22:0]); +assign fractb_00 = !(|opb[22:0]); + +assign fracta = {!expa_dn,opa[22:0]}; // Recover hidden bit +assign fractb = {!expb_dn,opb[22:0]}; // Recover hidden bit + +assign {co1,exp_tmp1} = op_div ? (expa - expb) : (expa + expb); +assign {co2,exp_tmp2} = op_div ? ({co1,exp_tmp1} + 8'h7f) : ({co1,exp_tmp1} - 8'h7f); + +assign exp_tmp3 = exp_tmp2 + 1; +assign exp_tmp4 = 8'h7f - exp_tmp1; +assign exp_tmp5 = op_div ? (exp_tmp4+1) : (exp_tmp4-1); + + +always@(posedge clk) + exp_out <= #1 op_div ? exp_out_div : exp_out_mul; + +assign exp_out_div = (expa_dn | expb_dn) ? (co2 ? exp_tmp5 : exp_tmp3 ) : co2 ? exp_tmp4 : exp_tmp2; +assign exp_out_mul = exp_ovf_d[1] ? exp_out_a : (expa_dn | expb_dn) ? exp_tmp3 : exp_tmp2; +assign exp_out_a = (expa_dn | expb_dn) ? exp_tmp5 : exp_tmp4; +assign exp_ovf_d[0] = op_div ? (expa[7] & !expb[7]) : (co2 & expa[7] & expb[7]); +assign exp_ovf_d[1] = op_div ? co2 : ((!expa[7] & !expb[7] & exp_tmp2[7]) | co2); + +always @(posedge clk) + exp_ovf <= #1 exp_ovf_d; + +assign underflow_d[0] = (exp_tmp1 < 8'h7f) & !co1 & !(opa_00 | opb_00 | expa_dn | expb_dn); +assign underflow_d[1] = ((expa[7] | expb[7]) & !opa_00 & !opb_00) | + (expa_dn & !fracta_00) | (expb_dn & !fractb_00); +assign underflow_d[2] = !opa_00 & !opb_00 & (exp_tmp1 == 8'h7f); + +always @(posedge clk) + underflow <= #1 underflow_d; + +always @(posedge clk) + inf <= #1 op_div ? (expb_dn & !expa[7]) : ({co1,exp_tmp1} > 9'h17e) ; + + +//////////////////////////////////////////////////////////////////////// +// +// Determine sign for the output +// + +// sign: 0=Posetive Number; 1=Negative Number +always @(signa or signb) + case({signa, signb}) // synopsys full_case parallel_case + 2'b0_0: sign_d = 0; + 2'b0_1: sign_d = 1; + 2'b1_0: sign_d = 1; + 2'b1_1: sign_d = 0; + endcase + +always @(posedge clk) + sign <= #1 sign_d; + +always @(posedge clk) + sign_exe <= #1 signa & signb; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/fpu/primitives.v b/verilog/rtl/fpu/primitives.v new file mode 100644 index 0000000..2e7f050 --- /dev/null +++ b/verilog/rtl/fpu/primitives.v
@@ -0,0 +1,103 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Primitives //// +//// FPU Primitives //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + + +`timescale 1ns / 100ps + + +//////////////////////////////////////////////////////////////////////// +// +// Add/Sub +// + +module add_sub27(add, opa, opb, sum, co); +input add; +input [26:0] opa, opb; +output [26:0] sum; +output co; + + + +assign {co, sum} = add ? (opa + opb) : (opa - opb); + +endmodule + +//////////////////////////////////////////////////////////////////////// +// +// Multiply +// + +module mul_r2(clk, opa, opb, prod); +input clk; +input [23:0] opa, opb; +output [47:0] prod; + +reg [47:0] prod1, prod; + +always @(posedge clk) + prod1 <= #1 opa * opb; + +always @(posedge clk) + prod <= #1 prod1; + +endmodule + +//////////////////////////////////////////////////////////////////////// +// +// Divide +// + +module div_r2(clk, opa, opb, quo, rem); +input clk; +input [49:0] opa; +input [23:0] opb; +output [49:0] quo, rem; + +reg [49:0] quo, rem, quo1, remainder; + +always @(posedge clk) + quo1 <= #1 opa / opb; + +always @(posedge clk) + quo <= #1 quo1; + +always @(posedge clk) + remainder <= #1 opa % opb; + +always @(posedge clk) + rem <= #1 remainder; + +endmodule + +
diff --git a/verilog/rtl/i2c/i2c_master_bit_ctrl.v b/verilog/rtl/i2c/i2c_master_bit_ctrl.v new file mode 100644 index 0000000..fbca97e --- /dev/null +++ b/verilog/rtl/i2c/i2c_master_bit_ctrl.v
@@ -0,0 +1,572 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master bit-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_bit_ctrl.v,v 1.14 2009-01-20 10:25:29 rherveille Exp $ +// +// $Date: 2009-01-20 10:25:29 $ +// $Revision: 1.14 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: $ +// Revision 1.14 2009/01/20 10:25:29 rherveille +// Added clock synchronization logic +// Fixed slave_wait signal +// +// Revision 1.13 2009/01/19 20:29:26 rherveille +// Fixed cr[0] register width +// Fixed ! usage instead of ~ +// Fixed bit controller parameter width to 18bits +// +// Revision 1.12 2006/09/04 09:08:13 rherveille +// fixed short scl high pulse after clock stretch +// fixed slave model not returning correct '(n)ack' signal +// +// Revision 1.11 2004/05/07 11:02:26 rherveille +// Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit. +// +// Revision 1.10 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.9 2003/03/10 14:26:37 rherveille +// Fixed cmd_ack generation item (no bug). +// +// Revision 1.8 2003/02/05 00:06:10 rherveille +// Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles. +// +// Revision 1.7 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.6 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.5 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.4 2002/10/30 18:10:07 rherveille +// Fixed some reported minor start/stop generation timing issuess. +// +// Revision 1.3 2002/06/15 07:37:03 rherveille +// Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment. +// +// Revision 1.2 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + +// +///////////////////////////////////// +// Bit controller section +///////////////////////////////////// +// +// Translate simple commands into SCL/SDA transitions +// Each command has 5 states, A/B/C/D/idle +// +// start: SCL ~~~~~~~~~~\____ +// SDA ~~~~~~~~\______ +// x | A | B | C | D | i +// +// repstart SCL ____/~~~~\___ +// SDA __/~~~\______ +// x | A | B | C | D | i +// +// stop SCL ____/~~~~~~~~ +// SDA ==\____/~~~~~ +// x | A | B | C | D | i +// +//- write SCL ____/~~~~\____ +// SDA ==X=========X= +// x | A | B | C | D | i +// +//- read SCL ____/~~~~\____ +// SDA XXXX=====XXXX +// x | A | B | C | D | i +// + +// Timing: Normal mode Fast mode +/////////////////////////////////////////////////////////////////////// +// Fscl 100KHz 400KHz +// Th_scl 4.0us 0.6us High period of SCL +// Tl_scl 4.7us 1.3us Low period of SCL +// Tsu:sta 4.7us 0.6us setup time for a repeated start condition +// Tsu:sto 4.0us 0.6us setup time for a stop conditon +// Tbuf 4.7us 1.3us Bus free time between a stop and start condition +// + + +`include "i2c_master_defines.v" + +module i2c_master_bit_ctrl ( + input clk, // system clock + input rst, // synchronous active high reset + input nReset, // asynchronous active low reset + input ena, // core enable signal + + input [15:0] clk_cnt, // clock prescale value + + input [ 3:0] cmd, // command (from byte controller) + output reg cmd_ack, // command complete acknowledge + output reg busy, // i2c bus busy + output reg al, // i2c bus arbitration lost + + input din, + output reg dout, + + input scl_i, // i2c clock line input + output scl_o, // i2c clock line output + output reg scl_oen, // i2c clock line output enable (active low) + input sda_i, // i2c data line input + output sda_o, // i2c data line output + output reg sda_oen // i2c data line output enable (active low) +); + + + // + // variable declarations + // + + reg [ 1:0] cSCL, cSDA; // capture SCL and SDA + reg [ 2:0] fSCL, fSDA; // SCL and SDA filter inputs + reg sSCL, sSDA; // filtered and synchronized SCL and SDA inputs + reg dSCL, dSDA; // delayed versions of sSCL and sSDA + reg dscl_oen; // delayed scl_oen + reg sda_chk; // check SDA output (Multi-master arbitration) + reg clk_en; // clock generation signals + reg slave_wait; // slave inserts wait states + reg [15:0] cnt; // clock divider counter (synthesis) + reg [13:0] filter_cnt; // clock divider for filter + + + // state machine variable + reg [17:0] c_state; + + // + // module body + // + + // whenever the slave is not ready it can delay the cycle by pulling SCL low + // delay scl_oen + always @(posedge clk) + dscl_oen <= #1 scl_oen; + + // slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low + // slave_wait remains asserted until the slave releases SCL + always @(posedge clk or negedge nReset) + if (!nReset) slave_wait <= 1'b0; + else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL); + + // master drives SCL high, but another master pulls it low + // master start counting down its low cycle now (clock synchronization) + wire scl_sync = dSCL & ~sSCL & scl_oen; + + + // generate clk enable signal + always @(posedge clk or negedge nReset) + if (~nReset) + begin + cnt <= #1 16'h0; + clk_en <= #1 1'b1; + end + else if (rst || ~|cnt || !ena || scl_sync) + begin + cnt <= #1 clk_cnt; + clk_en <= #1 1'b1; + end + else if (slave_wait) + begin + cnt <= #1 cnt; + clk_en <= #1 1'b0; + end + else + begin + cnt <= #1 cnt - 16'h1; + clk_en <= #1 1'b0; + end + + + // generate bus status controller + + // capture SDA and SCL + // reduce metastability risk + always @(posedge clk or negedge nReset) + if (!nReset) + begin + cSCL <= #1 2'b00; + cSDA <= #1 2'b00; + end + else if (rst) + begin + cSCL <= #1 2'b00; + cSDA <= #1 2'b00; + end + else + begin + cSCL <= {cSCL[0],scl_i}; + cSDA <= {cSDA[0],sda_i}; + end + + + // filter SCL and SDA signals; (attempt to) remove glitches + always @(posedge clk or negedge nReset) + if (!nReset ) filter_cnt <= 14'h0; + else if (rst || !ena ) filter_cnt <= 14'h0; + else if (~|filter_cnt) filter_cnt <= clk_cnt >> 2; //16x I2C bus frequency + else filter_cnt <= filter_cnt -1; + + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + fSCL <= 3'b111; + fSDA <= 3'b111; + end + else if (rst) + begin + fSCL <= 3'b111; + fSDA <= 3'b111; + end + else if (~|filter_cnt) + begin + fSCL <= {fSCL[1:0],cSCL[1]}; + fSDA <= {fSDA[1:0],cSDA[1]}; + end + + + // generate filtered SCL and SDA signals + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sSCL <= #1 1'b1; + sSDA <= #1 1'b1; + + dSCL <= #1 1'b1; + dSDA <= #1 1'b1; + end + else if (rst) + begin + sSCL <= #1 1'b1; + sSDA <= #1 1'b1; + + dSCL <= #1 1'b1; + dSDA <= #1 1'b1; + end + else + begin + sSCL <= #1 &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]); + sSDA <= #1 &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]); + + dSCL <= #1 sSCL; + dSDA <= #1 sSDA; + end + + // detect start condition => detect falling edge on SDA while SCL is high + // detect stop condition => detect rising edge on SDA while SCL is high + reg sta_condition; + reg sto_condition; + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sta_condition <= #1 1'b0; + sto_condition <= #1 1'b0; + end + else if (rst) + begin + sta_condition <= #1 1'b0; + sto_condition <= #1 1'b0; + end + else + begin + sta_condition <= #1 ~sSDA & dSDA & sSCL; + sto_condition <= #1 sSDA & ~dSDA & sSCL; + end + + + // generate i2c bus busy signal + always @(posedge clk or negedge nReset) + if (!nReset) busy <= #1 1'b0; + else if (rst ) busy <= #1 1'b0; + else busy <= #1 (sta_condition | busy) & ~sto_condition; + + + // generate arbitration lost signal + // aribitration lost when: + // 1) master drives SDA high, but the i2c bus is low + // 2) stop detected while not requested + reg cmd_stop; + always @(posedge clk or negedge nReset) + if (~nReset) + cmd_stop <= #1 1'b0; + else if (rst) + cmd_stop <= #1 1'b0; + else if (clk_en) + cmd_stop <= #1 cmd == `I2C_CMD_STOP; + + always @(posedge clk or negedge nReset) + if (~nReset) + al <= #1 1'b0; + else if (rst) + al <= #1 1'b0; + else + al <= #1 (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop); + + + // generate dout signal (store SDA on rising edge of SCL) + always @(posedge clk) + if (sSCL & ~dSCL) dout <= #1 sSDA; + + + // generate statemachine + + // nxt_state decoder + parameter [17:0] idle = 18'b0_0000_0000_0000_0000; + parameter [17:0] start_a = 18'b0_0000_0000_0000_0001; + parameter [17:0] start_b = 18'b0_0000_0000_0000_0010; + parameter [17:0] start_c = 18'b0_0000_0000_0000_0100; + parameter [17:0] start_d = 18'b0_0000_0000_0000_1000; + parameter [17:0] start_e = 18'b0_0000_0000_0001_0000; + parameter [17:0] stop_a = 18'b0_0000_0000_0010_0000; + parameter [17:0] stop_b = 18'b0_0000_0000_0100_0000; + parameter [17:0] stop_c = 18'b0_0000_0000_1000_0000; + parameter [17:0] stop_d = 18'b0_0000_0001_0000_0000; + parameter [17:0] rd_a = 18'b0_0000_0010_0000_0000; + parameter [17:0] rd_b = 18'b0_0000_0100_0000_0000; + parameter [17:0] rd_c = 18'b0_0000_1000_0000_0000; + parameter [17:0] rd_d = 18'b0_0001_0000_0000_0000; + parameter [17:0] wr_a = 18'b0_0010_0000_0000_0000; + parameter [17:0] wr_b = 18'b0_0100_0000_0000_0000; + parameter [17:0] wr_c = 18'b0_1000_0000_0000_0000; + parameter [17:0] wr_d = 18'b1_0000_0000_0000_0000; + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + sda_chk <= #1 1'b0; + end + else if (rst | al) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + sda_chk <= #1 1'b0; + end + else + begin + cmd_ack <= #1 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle + + if (clk_en) + case (c_state) + // idle state + idle: + begin + case (cmd) + `I2C_CMD_START: c_state <= #1 start_a; + `I2C_CMD_STOP: c_state <= #1 stop_a; + `I2C_CMD_WRITE: c_state <= #1 wr_a; + `I2C_CMD_READ: c_state <= #1 rd_a; + default: c_state <= #1 idle; + endcase + + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 sda_oen; // keep SDA in same state + sda_chk <= #1 1'b0; // don't check SDA output + end + + // start + start_a: + begin + c_state <= #1 start_b; + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 1'b1; // set SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_b: + begin + c_state <= #1 start_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_c: + begin + c_state <= #1 start_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // set SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_d: + begin + c_state <= #1 start_e; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_e: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + // stop + stop_a: + begin + c_state <= #1 stop_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b0; // set SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_b: + begin + c_state <= #1 stop_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_c: + begin + c_state <= #1 stop_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; // set SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + // read + rd_a: + begin + c_state <= #1 rd_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b1; // tri-state SDA + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_b: + begin + c_state <= #1 rd_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_c: + begin + c_state <= #1 rd_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + // write + wr_a: + begin + c_state <= #1 wr_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 din; // set SDA + sda_chk <= #1 1'b0; // don't check SDA output (SCL low) + end + + wr_b: + begin + c_state <= #1 wr_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 din; // keep SDA + sda_chk <= #1 1'b0; // don't check SDA output yet + // allow some time for SDA and SCL to settle + end + + wr_c: + begin + c_state <= #1 wr_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 din; + sda_chk <= #1 1'b1; // check SDA output + end + + wr_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 din; + sda_chk <= #1 1'b0; // don't check SDA output (SCL low) + end + + endcase + end + + + // assign scl and sda output (always gnd) + assign scl_o = 1'b0; + assign sda_o = 1'b0; + +endmodule
diff --git a/verilog/rtl/i2c/i2c_master_byte_ctrl.v b/verilog/rtl/i2c/i2c_master_byte_ctrl.v new file mode 100644 index 0000000..16dfb82 --- /dev/null +++ b/verilog/rtl/i2c/i2c_master_byte_ctrl.v
@@ -0,0 +1,342 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master byte-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_byte_ctrl.v,v 1.8 2009-01-19 20:29:26 rherveille Exp $ +// +// $Date: 2009-01-19 20:29:26 $ +// $Revision: 1.8 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.7 2004/02/18 11:40:46 rherveille +// Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command. +// +// Revision 1.6 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.5 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.4 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.3 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + + +`include "i2c_master_defines.v" + +module i2c_master_byte_ctrl ( + clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din, + cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); + + // + // inputs & outputs + // + input clk; // master clock + input rst; // synchronous active high reset + input nReset; // asynchronous active low reset + input ena; // core enable signal + + input [15:0] clk_cnt; // 4x SCL + + // control inputs + input start; + input stop; + input read; + input write; + input ack_in; + input [7:0] din; + + // status outputs + output cmd_ack; + reg cmd_ack; + output ack_out; + reg ack_out; + output i2c_busy; + output i2c_al; + output [7:0] dout; + + // I2C signals + input scl_i; + output scl_o; + output scl_oen; + input sda_i; + output sda_o; + output sda_oen; + + + // + // Variable declarations + // + + // statemachine + parameter [4:0] ST_IDLE = 5'b0_0000; + parameter [4:0] ST_START = 5'b0_0001; + parameter [4:0] ST_READ = 5'b0_0010; + parameter [4:0] ST_WRITE = 5'b0_0100; + parameter [4:0] ST_ACK = 5'b0_1000; + parameter [4:0] ST_STOP = 5'b1_0000; + + // signals for bit_controller + reg [3:0] core_cmd; + reg core_txd; + wire core_ack, core_rxd; + + // signals for shift register + reg [7:0] sr; //8bit shift register + reg shift, ld; + + // signals for state machine + wire go; + reg [2:0] dcnt; + wire cnt_done; + + // + // Module body + // + + // hookup bit_controller + i2c_master_bit_ctrl bit_controller ( + .clk ( clk ), + .rst ( rst ), + .nReset ( nReset ), + .ena ( ena ), + .clk_cnt ( clk_cnt ), + .cmd ( core_cmd ), + .cmd_ack ( core_ack ), + .busy ( i2c_busy ), + .al ( i2c_al ), + .din ( core_txd ), + .dout ( core_rxd ), + .scl_i ( scl_i ), + .scl_o ( scl_o ), + .scl_oen ( scl_oen ), + .sda_i ( sda_i ), + .sda_o ( sda_o ), + .sda_oen ( sda_oen ) + ); + + // generate go-signal + assign go = (read | write | stop) & ~cmd_ack; + + // assign dout output to shift-register + assign dout = sr; + + // generate shift register + always @(posedge clk or negedge nReset) + if (!nReset) + sr <= #1 8'h0; + else if (rst) + sr <= #1 8'h0; + else if (ld) + sr <= #1 din; + else if (shift) + sr <= #1 {sr[6:0], core_rxd}; + + // generate counter + always @(posedge clk or negedge nReset) + if (!nReset) + dcnt <= #1 3'h0; + else if (rst) + dcnt <= #1 3'h0; + else if (ld) + dcnt <= #1 3'h7; + else if (shift) + dcnt <= #1 dcnt - 3'h1; + + assign cnt_done = ~(|dcnt); + + // + // state machine + // + reg [4:0] c_state; + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + core_cmd <= #1 `I2C_CMD_NOP; + core_txd <= #1 1'b0; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + c_state <= #1 ST_IDLE; + ack_out <= #1 1'b0; + end + else if (rst | i2c_al) + begin + core_cmd <= #1 `I2C_CMD_NOP; + core_txd <= #1 1'b0; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + c_state <= #1 ST_IDLE; + ack_out <= #1 1'b0; + end + else + begin + // initially reset all signals + core_txd <= #1 sr[7]; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + + case (c_state) + ST_IDLE: + if (go) + begin + if (start) + begin + c_state <= #1 ST_START; + core_cmd <= #1 `I2C_CMD_START; + end + else if (read) + begin + c_state <= #1 ST_READ; + core_cmd <= #1 `I2C_CMD_READ; + end + else if (write) + begin + c_state <= #1 ST_WRITE; + core_cmd <= #1 `I2C_CMD_WRITE; + end + else // stop + begin + c_state <= #1 ST_STOP; + core_cmd <= #1 `I2C_CMD_STOP; + end + + ld <= #1 1'b1; + end + + ST_START: + if (core_ack) + begin + if (read) + begin + c_state <= #1 ST_READ; + core_cmd <= #1 `I2C_CMD_READ; + end + else + begin + c_state <= #1 ST_WRITE; + core_cmd <= #1 `I2C_CMD_WRITE; + end + + ld <= #1 1'b1; + end + + ST_WRITE: + if (core_ack) + if (cnt_done) + begin + c_state <= #1 ST_ACK; + core_cmd <= #1 `I2C_CMD_READ; + end + else + begin + c_state <= #1 ST_WRITE; // stay in same state + core_cmd <= #1 `I2C_CMD_WRITE; // write next bit + shift <= #1 1'b1; + end + + ST_READ: + if (core_ack) + begin + if (cnt_done) + begin + c_state <= #1 ST_ACK; + core_cmd <= #1 `I2C_CMD_WRITE; + end + else + begin + c_state <= #1 ST_READ; // stay in same state + core_cmd <= #1 `I2C_CMD_READ; // read next bit + end + + shift <= #1 1'b1; + core_txd <= #1 ack_in; + end + + ST_ACK: + if (core_ack) + begin + if (stop) + begin + c_state <= #1 ST_STOP; + core_cmd <= #1 `I2C_CMD_STOP; + end + else + begin + c_state <= #1 ST_IDLE; + core_cmd <= #1 `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= #1 1'b1; + end + + // assign ack_out output to bit_controller_rxd (contains last received bit) + ack_out <= #1 core_rxd; + + core_txd <= #1 1'b1; + end + else + core_txd <= #1 ack_in; + + ST_STOP: + if (core_ack) + begin + c_state <= #1 ST_IDLE; + core_cmd <= #1 `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= #1 1'b1; + end + + endcase + end +endmodule
diff --git a/verilog/rtl/i2c/i2c_master_defines.v b/verilog/rtl/i2c/i2c_master_defines.v new file mode 100644 index 0000000..0338aa0 --- /dev/null +++ b/verilog/rtl/i2c/i2c_master_defines.v
@@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master controller defines //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_defines.v,v 1.3 2001-11-05 11:59:25 rherveille Exp $ +// +// $Date: 2001-11-05 11:59:25 $ +// $Revision: 1.3 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ + + +// I2C registers wishbone addresses + +// bitcontroller states +`define I2C_CMD_NOP 4'b0000 +`define I2C_CMD_START 4'b0001 +`define I2C_CMD_STOP 4'b0010 +`define I2C_CMD_WRITE 4'b0100 +`define I2C_CMD_READ 4'b1000
diff --git a/verilog/rtl/i2c/i2c_master_top.v b/verilog/rtl/i2c/i2c_master_top.v new file mode 100644 index 0000000..02c8884 --- /dev/null +++ b/verilog/rtl/i2c/i2c_master_top.v
@@ -0,0 +1,299 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE revB.2 compliant I2C Master controller Top-level //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_top.v,v 1.12 2009-01-19 20:29:26 rherveille Exp $ +// +// $Date: 2009-01-19 20:29:26 $ +// $Revision: 1.12 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// Revision 1.11 2005/02/27 09:26:24 rherveille +// Fixed register overwrite issue. +// Removed full_case pragma, replaced it by a default statement. +// +// Revision 1.10 2003/09/01 10:34:38 rherveille +// Fix a blocking vs. non-blocking error in the wb_dat output mux. +// +// Revision 1.9 2003/01/09 16:44:45 rherveille +// Fixed a bug in the Command Register declaration. +// +// Revision 1.8 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.7 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.6 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.5 2001/11/10 10:52:55 rherveille +// Changed PRER reset value from 0x0000 to 0xffff, conform specs. +// + + +`include "i2c_master_defines.v" + +module i2c_master_top( + wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o, + wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o, + scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o ); + + // parameters + parameter ARST_LVL = 1'b0; // asynchronous reset level + + // + // inputs & outputs + // + + // wishbone signals + input wb_clk_i; // master clock input + input wb_rst_i; // synchronous active high reset + input arst_i; // asynchronous reset + input [2:0] wb_adr_i; // lower address bits + input [7:0] wb_dat_i; // databus input + output [7:0] wb_dat_o; // databus output + input wb_we_i; // write enable input + input wb_stb_i; // stobe/core select signal + input wb_cyc_i; // valid bus cycle input + output wb_ack_o; // bus cycle acknowledge output + output wb_inta_o; // interrupt request signal output + + reg [7:0] wb_dat_o; + reg wb_ack_o; + reg wb_inta_o; + + // I2C signals + // i2c clock line + input scl_pad_i; // SCL-line input + output scl_pad_o; // SCL-line output (always 1'b0) + output scl_padoen_o; // SCL-line output enable (active low) + + // i2c data line + input sda_pad_i; // SDA-line input + output sda_pad_o; // SDA-line output (always 1'b0) + output sda_padoen_o; // SDA-line output enable (active low) + + + // + // variable declarations + // + + // registers + reg [15:0] prer; // clock prescale register + reg [ 7:0] ctr; // control register + reg [ 7:0] txr; // transmit register + wire [ 7:0] rxr; // receive register + reg [ 7:0] cr; // command register + wire [ 7:0] sr; // status register + + // done signal: command completed, clear command register + wire done; + + // core enable signal + wire core_en; + wire ien; + + // status register signals + wire irxack; + reg rxack; // received aknowledge from slave + reg tip; // transfer in progress + reg irq_flag; // interrupt pending flag + wire i2c_busy; // bus busy (start signal detected) + wire i2c_al; // i2c bus arbitration lost + reg al; // status register arbitration lost bit + + // + // module body + // + + // generate internal reset + wire rst_i = arst_i ^ ARST_LVL; + + // generate wishbone signals + //wire wb_wacc = wb_we_i & wb_ack_o; + wire wb_wacc = wb_we_i & wb_stb_i & wb_cyc_i ; + + // generate acknowledge output signal + always @(posedge wb_clk_i) + wb_ack_o <= #1 wb_cyc_i & wb_stb_i & ~wb_ack_o; // because timing is always honored + + // assign DAT_O + always @(posedge wb_clk_i) + begin + case (wb_adr_i) + 3'b000: wb_dat_o <= #1 prer[ 7:0]; + 3'b001: wb_dat_o <= #1 prer[15:8]; + 3'b010: wb_dat_o <= #1 ctr; + 3'b011: wb_dat_o <= #1 rxr; // write is transmit register (txr) + 3'b100: wb_dat_o <= #1 sr; // write is command register (cr) + 3'b101: wb_dat_o <= #1 txr; + 3'b110: wb_dat_o <= #1 cr; + 3'b111: wb_dat_o <= #1 0; // reserved + endcase + end + + // generate registers + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + prer <= #1 16'hffff; + ctr <= #1 8'h0; + txr <= #1 8'h0; + end + else if (wb_rst_i) + begin + prer <= #1 16'hffff; + ctr <= #1 8'h0; + txr <= #1 8'h0; + end + else + if (wb_wacc) + case (wb_adr_i) + 3'b000 : prer [ 7:0] <= #1 wb_dat_i; + 3'b001 : prer [15:8] <= #1 wb_dat_i; + 3'b010 : ctr <= #1 wb_dat_i; + 3'b011 : txr <= #1 wb_dat_i; + default: ; + endcase + + // generate command register (special case) + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + cr <= #1 8'h0; + else if (wb_rst_i) + cr <= #1 8'h0; + else if (wb_wacc) + begin + if (core_en & (wb_adr_i == 3'b100) ) + cr <= #1 wb_dat_i; + end + else + begin + if (done | i2c_al) + cr[7:4] <= #1 4'h0; // clear command bits when done + // or when aribitration lost + cr[2:1] <= #1 2'b0; // reserved bits + cr[0] <= #1 1'b0; // clear IRQ_ACK bit + end + + + // decode command register + wire sta = cr[7]; + wire sto = cr[6]; + wire rd = cr[5]; + wire wr = cr[4]; + wire ack = cr[3]; + wire iack = cr[0]; + + // decode control register + assign core_en = ctr[7]; + assign ien = ctr[6]; + + // hookup byte controller block + i2c_master_byte_ctrl byte_controller ( + .clk ( wb_clk_i ), + .rst ( wb_rst_i ), + .nReset ( rst_i ), + .ena ( core_en ), + .clk_cnt ( prer ), + .start ( sta ), + .stop ( sto ), + .read ( rd ), + .write ( wr ), + .ack_in ( ack ), + .din ( txr ), + .cmd_ack ( done ), + .ack_out ( irxack ), + .dout ( rxr ), + .i2c_busy ( i2c_busy ), + .i2c_al ( i2c_al ), + .scl_i ( scl_pad_i ), + .scl_o ( scl_pad_o ), + .scl_oen ( scl_padoen_o ), + .sda_i ( sda_pad_i ), + .sda_o ( sda_pad_o ), + .sda_oen ( sda_padoen_o ) + ); + + // status register block + interrupt request signal + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + al <= #1 1'b0; + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else if (wb_rst_i) + begin + al <= #1 1'b0; + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else + begin + al <= #1 i2c_al | (al & ~sta); + rxack <= #1 irxack; + tip <= #1 (rd | wr); + irq_flag <= #1 (done | i2c_al | irq_flag) & ~iack; // interrupt request flag is always generated + end + + // generate interrupt request signals + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + wb_inta_o <= #1 1'b0; + else if (wb_rst_i) + wb_inta_o <= #1 1'b0; + else + wb_inta_o <= #1 irq_flag && ien; // interrupt signal is only generated when IEN (interrupt enable bit is set) + + // assign status register bits + assign sr[7] = rxack; + assign sr[6] = i2c_busy; + assign sr[5] = al; + assign sr[4:2] = 3'h0; // reserved + assign sr[1] = tip; + assign sr[0] = irq_flag; + +endmodule
diff --git a/verilog/rtl/i2c/timescale.v b/verilog/rtl/i2c/timescale.v new file mode 100644 index 0000000..c2dae7a --- /dev/null +++ b/verilog/rtl/i2c/timescale.v
@@ -0,0 +1,3 @@ +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +`timescale 1ns / 10ps +
diff --git a/verilog/rtl/ptc/ptc_defines.v b/verilog/rtl/ptc/ptc_defines.v new file mode 100644 index 0000000..7e13f09 --- /dev/null +++ b/verilog/rtl/ptc/ptc_defines.v
@@ -0,0 +1,164 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE PWM/Timer/Counter Definitions //// +//// //// +//// This file is part of the PTC project //// +//// http://www.opencores.org/cores/ptc/ //// +//// //// +//// Description //// +//// PTC definitions. //// +//// //// +//// To Do: //// +//// Nothing //// +//// //// +//// Author(s): //// +//// - Damjan Lampret, lampret@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: not supported by cvs2svn $ +// Revision 1.2 2001/08/21 23:23:50 lampret +// Changed directory structure, defines and port names. +// +// Revision 1.2 2001/07/17 00:18:08 lampret +// Added new parameters however RTL still has some issues related to hrc_match and int_match +// +// Revision 1.1 2001/06/05 07:45:36 lampret +// Added initial RTL and test benches. There are still some issues with these files. +// +// + +// +// Width of the PTC counter +// +// +`define PTC_CW 32 + +// +// Undefine this one if you don't want to remove PTC block from your design +// but you also don't need it. When it is undefined, all PTC ports still +// remain valid and the core can be synthesized however internally there is +// no PTC funationality. +// +// Defined by default (duhh !). +// +`define PTC_IMPLEMENTED + +// +// Undefine if you don't need to read PTC registers. +// When it is undefined all reads of PTC registers return zero. This +// is usually useful if you want really small area (for example when +// implemented in FPGA). +// +// To follow PTC IP core specification document this one must be defined. +// Also to successfully run the test bench it must be defined. By default +// it is defined. +// +`define PTC_READREGS + +// +// Full WISHBONE address decoding +// +// It is is undefined, partial WISHBONE address decoding is performed. +// Undefine it if you need to save some area. +// +// By default it is defined. +// +`define PTC_FULL_DECODE + +// +// Strict 32-bit WISHBONE access +// +// If this one is defined, all WISHBONE accesses must be 32-bit. If it is +// not defined, err_o is asserted whenever 8- or 16-bit access is made. +// Undefine it if you need to save some area. +// +// By default it is defined. +// +`define PTC_STRICT_32BIT_ACCESS + +// +// WISHBONE address bits used for full decoding of PTC registers. +// +`define PTC_ADDRHH 15 +`define PTC_ADDRHL 5 +`define PTC_ADDRLH 1 +`define PTC_ADDRLL 0 + +// +// Bits of WISHBONE address used for partial decoding of PTC registers. +// +// Default 4:2. +// +`define PTC_OFS_BITS `PTC_ADDRHL-1:`PTC_ADDRLH+1 + +// +// Addresses of PTC registers +// +// To comply with PTC IP core specification document they must go from +// address 0 to address 0xC in the following order: RPTC_CNTR, RPTC_HRC, +// RPTC_LRC and RPTC_CTRL +// +// If particular alarm/ctrl register is not needed, it's address definition +// can be omitted and the register will not be implemented. Instead a fixed +// default value will +// be used. +// +`define PTC_RPTC_CNTR 2'h0 // Address 0x0 +`define PTC_RPTC_HRC 2'h1 // Address 0x4 +`define PTC_RPTC_LRC 2'h2 // Address 0x8 +`define PTC_RPTC_CTRL 2'h3 // Address 0xc + +// +// Default values for unimplemented PTC registers +// +`define PTC_DEF_RPTC_CNTR `PTC_CW'b0 +`define PTC_DEF_RPTC_HRC `PTC_CW'b0 +`define PTC_DEF_RPTC_LRC `PTC_CW'b0 +`define PTC_DEF_RPTC_CTRL 9'h01 // RPTC_CTRL[EN] = 1 + +// +// RPTC_CTRL bits +// +// To comply with the PTC IP core specification document they must go from +// bit 0 to bit 8 in the following order: EN, ECLK, NEC, OE, SINGLE, INTE, +// INT, CNTRRST, CAPTE +// +`define PTC_RPTC_CTRL_EN 0 +`define PTC_RPTC_CTRL_ECLK 1 +`define PTC_RPTC_CTRL_NEC 2 +`define PTC_RPTC_CTRL_OE 3 +`define PTC_RPTC_CTRL_SINGLE 4 +`define PTC_RPTC_CTRL_INTE 5 +`define PTC_RPTC_CTRL_INT 6 +`define PTC_RPTC_CTRL_CNTRRST 7 +`define PTC_RPTC_CTRL_CAPTE 8 +
diff --git a/verilog/rtl/ptc/ptc_top.v b/verilog/rtl/ptc/ptc_top.v new file mode 100644 index 0000000..4b144f0 --- /dev/null +++ b/verilog/rtl/ptc/ptc_top.v
@@ -0,0 +1,392 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE PWM/Timer/Counter //// +//// //// +//// This file is part of the PTC project //// +//// http://www.opencores.org/cores/ptc/ //// +//// //// +//// Description //// +//// Implementation of PWM/Timer/Counter IP core according to //// +//// PTC IP core specification document. //// +//// //// +//// To Do: //// +//// Nothing //// +//// //// +//// Author(s): //// +//// - Damjan Lampret, lampret@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +//// SPDX-License-Identifier: LGPL-2.1-or-later //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: not supported by cvs2svn $ +// Revision 1.4 2001/09/18 18:48:29 lampret +// Changed top level ptc into ptc_top. Changed defines.v into ptc_defines.v. Reset of the counter is now synchronous. +// +// Revision 1.3 2001/08/21 23:23:50 lampret +// Changed directory structure, defines and port names. +// +// Revision 1.2 2001/07/17 00:18:10 lampret +// Added new parameters however RTL still has some issues related to hrc_match and int_match +// +// Revision 1.1 2001/06/05 07:45:36 lampret +// Added initial RTL and test benches. There are still some issues with these files. +// +// + +`include "ptc_defines.v" + +module ptc_top( + // WISHBONE Interface + wb_clk_i, wb_rst_i, wb_cyc_i, wb_adr_i, wb_dat_i, wb_sel_i, wb_we_i, wb_stb_i, + wb_dat_o, wb_ack_o, wb_err_o, wb_inta_o, + + // External PTC Interface + gate_clk_pad_i, capt_pad_i, pwm_pad_o, oen_padoen_o +); + +parameter dw = 32; +parameter aw = `PTC_ADDRHH+1; +parameter cw = `PTC_CW; + +// +// WISHBONE Interface +// +input wb_clk_i; // Clock +input wb_rst_i; // Reset +input wb_cyc_i; // cycle valid input +input [aw-1:0] wb_adr_i; // address bus inputs +input [dw-1:0] wb_dat_i; // input data bus +input [3:0] wb_sel_i; // byte select inputs +input wb_we_i; // indicates write transfer +input wb_stb_i; // strobe input +output [dw-1:0] wb_dat_o; // output data bus +output wb_ack_o; // normal termination +output wb_err_o; // termination w/ error +output wb_inta_o; // Interrupt request output + +// +// External PTC Interface +// +input gate_clk_pad_i; // EClk/Gate input +input capt_pad_i; // Capture input +output pwm_pad_o; // PWM output +output oen_padoen_o; // PWM output driver enable + +`ifdef PTC_IMPLEMENTED + +// +// PTC Main Counter Register (or no register) +// +`ifdef PTC_RPTC_CNTR +reg [cw-1:0] rptc_cntr; // RPTC_CNTR register +`else +wire [cw-1:0] rptc_cntr; // No RPTC_CNTR register +`endif + +// +// PTC HI Reference/Capture Register (or no register) +// +`ifdef PTC_RPTC_HRC +reg [cw-1:0] rptc_hrc; // RPTC_HRC register +`else +wire [cw-1:0] rptc_hrc; // No RPTC_HRC register +`endif + +// +// PTC LO Reference/Capture Register (or no register) +// +`ifdef PTC_RPTC_LRC +reg [cw-1:0] rptc_lrc; // RPTC_LRC register +`else +wire [cw-1:0] rptc_lrc; // No RPTC_LRC register +`endif + +// +// PTC Control Register (or no register) +// +`ifdef PTC_RPTC_CTRL +reg [8:0] rptc_ctrl; // RPTC_CTRL register +`else +wire [8:0] rptc_ctrl; // No RPTC_CTRL register +`endif + +// +// Internal wires & regs +// +wire rptc_cntr_sel; // RPTC_CNTR select +wire rptc_hrc_sel; // RPTC_HRC select +wire rptc_lrc_sel; // RPTC_LRC select +wire rptc_ctrl_sel; // RPTC_CTRL select +wire hrc_match; // RPTC_HRC matches RPTC_CNTR +wire lrc_match; // RPTC_LRC matches RPTC_CNTR +wire restart; // Restart counter when asserted +wire stop; // Stop counter when asserted +wire cntr_clk; // Counter clock +wire cntr_rst; // Counter reset +wire hrc_clk; // RPTC_HRC clock +wire lrc_clk; // RPTC_LRC clock +wire eclk_gate; // ptc_ecgt xored by RPTC_CTRL[NEC] +wire gate; // Gate function of ptc_ecgt +wire pwm_rst; // Reset of a PWM output +reg [dw-1:0] wb_dat_o; // Data out +reg pwm_pad_o; // PWM output +reg intr_reg; // Interrupt reg +wire int_match; // Interrupt match +wire full_decoding; // Full address decoding qualification + +// +// All WISHBONE transfer terminations are successful except when: +// a) full address decoding is enabled and address doesn't match +// any of the PTC registers +// b) sel_i evaluation is enabled and one of the sel_i inputs is zero +// +assign wb_ack_o = wb_cyc_i & wb_stb_i & !wb_err_o; +`ifdef PTC_FULL_DECODE +`ifdef PTC_STRICT_32BIT_ACCESS +assign wb_err_o = wb_cyc_i & wb_stb_i & (!full_decoding | (wb_sel_i != 4'b1111)); +`else +assign wb_err_o = wb_cyc_i & wb_stb_i & !full_decoding; +`endif +`else +`ifdef PTC_STRICT_32BIT_ACCESS +assign wb_err_o = wb_cyc_i & wb_stb_i & (wb_sel_i != 4'b1111); +`else +assign wb_err_o = 1'b0; +`endif +`endif + +// +// Counter clock is selected by RPTC_CTRL[ECLK]. When it is set, +// external clock is used. +// +assign cntr_clk = rptc_ctrl[`PTC_RPTC_CTRL_ECLK] ? eclk_gate : wb_clk_i; + +// +// Counter reset +// +assign cntr_rst = wb_rst_i; + +// +// HRC clock is selected by RPTC_CTRL[CAPTE]. When it is set, +// ptc_capt is used as a clock. +// +assign hrc_clk = rptc_ctrl[`PTC_RPTC_CTRL_CAPTE] ? capt_pad_i : wb_clk_i; + +// +// LRC clock is selected by RPTC_CTRL[CAPTE]. When it is set, +// inverted ptc_capt is used as a clock. +// +assign lrc_clk = rptc_ctrl[`PTC_RPTC_CTRL_CAPTE] ? ~capt_pad_i : wb_clk_i; + +// +// PWM output driver enable is inverted RPTC_CTRL[OE] +// +assign oen_padoen_o = ~rptc_ctrl[`PTC_RPTC_CTRL_OE]; + +// +// Use RPTC_CTRL[NEC] +// +assign eclk_gate = gate_clk_pad_i ^ rptc_ctrl[`PTC_RPTC_CTRL_NEC]; + +// +// Gate function is active when RPTC_CTRL[ECLK] is cleared +// +assign gate = eclk_gate & ~rptc_ctrl[`PTC_RPTC_CTRL_ECLK]; + +// +// Full address decoder +// +`ifdef PTC_FULL_DECODE +assign full_decoding = (wb_adr_i[`PTC_ADDRHH:`PTC_ADDRHL] == {`PTC_ADDRHH-`PTC_ADDRHL+1{1'b0}}) & + (wb_adr_i[`PTC_ADDRLH:`PTC_ADDRLL] == {`PTC_ADDRLH-`PTC_ADDRLL+1{1'b0}}); +`else +assign full_decoding = 1'b1; +`endif + +// +// PTC registers address decoder +// +assign rptc_cntr_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`PTC_OFS_BITS] == `PTC_RPTC_CNTR) & full_decoding; +assign rptc_hrc_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`PTC_OFS_BITS] == `PTC_RPTC_HRC) & full_decoding; +assign rptc_lrc_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`PTC_OFS_BITS] == `PTC_RPTC_LRC) & full_decoding; +assign rptc_ctrl_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`PTC_OFS_BITS] == `PTC_RPTC_CTRL) & full_decoding; + +// +// Write to RPTC_CTRL or update of RPTC_CTRL[INT] bit +// +`ifdef PTC_RPTC_CTRL +always @(posedge wb_clk_i or posedge wb_rst_i) + if (wb_rst_i) + rptc_ctrl <= #1 9'b0; + else if (rptc_ctrl_sel && wb_we_i) + rptc_ctrl <= #1 wb_dat_i[8:0]; + else if (rptc_ctrl[`PTC_RPTC_CTRL_INTE]) + rptc_ctrl[`PTC_RPTC_CTRL_INT] <= #1 rptc_ctrl[`PTC_RPTC_CTRL_INT] | intr_reg; +`else +assign rptc_ctrl = `PTC_DEF_RPTC_CTRL; +`endif + +// +// Write to RPTC_HRC +// +`ifdef PTC_RPTC_HRC +always @(posedge hrc_clk or posedge wb_rst_i) + if (wb_rst_i) + rptc_hrc <= #1 {cw{1'b0}}; + else if (rptc_hrc_sel && wb_we_i) + rptc_hrc <= #1 wb_dat_i[cw-1:0]; + else if (rptc_ctrl[`PTC_RPTC_CTRL_CAPTE]) + rptc_hrc <= #1 rptc_cntr; +`else +assign rptc_hrc = `DEF_RPTC_HRC; +`endif + +// +// Write to RPTC_LRC +// +`ifdef PTC_RPTC_LRC +always @(posedge lrc_clk or posedge wb_rst_i) + if (wb_rst_i) + rptc_lrc <= #1 {cw{1'b0}}; + else if (rptc_lrc_sel && wb_we_i) + rptc_lrc <= #1 wb_dat_i[cw-1:0]; + else if (rptc_ctrl[`PTC_RPTC_CTRL_CAPTE]) + rptc_lrc <= #1 rptc_cntr; +`else +assign rptc_lrc = `DEF_RPTC_LRC; +`endif + +// +// Write to or increment of RPTC_CNTR +// +`ifdef PTC_RPTC_CNTR +always @(posedge cntr_clk or posedge cntr_rst) + if (cntr_rst) + rptc_cntr <= #1 {cw{1'b0}}; + else if (rptc_cntr_sel && wb_we_i) + rptc_cntr <= #1 wb_dat_i[cw-1:0]; + else if (restart) + rptc_cntr <= #1 {cw{1'b0}}; + else if (!stop && rptc_ctrl[`PTC_RPTC_CTRL_EN] && !gate) + rptc_cntr <= #1 rptc_cntr + 1; +`else +assign rptc_cntr = `DEF_RPTC_CNTR; +`endif + +// +// Read PTC registers +// +always @(wb_adr_i or rptc_hrc or rptc_lrc or rptc_ctrl or rptc_cntr) + case (wb_adr_i[`PTC_OFS_BITS]) +`ifdef PTC_READREGS + `PTC_RPTC_HRC: wb_dat_o[dw-1:0] = {{dw-cw{1'b0}}, rptc_hrc}; + `PTC_RPTC_LRC: wb_dat_o[dw-1:0] = {{dw-cw{1'b0}}, rptc_lrc}; + `PTC_RPTC_CTRL: wb_dat_o[dw-1:0] = {{dw-9{1'b0}}, rptc_ctrl}; +`endif + default: wb_dat_o[dw-1:0] = {{dw-cw{1'b0}}, rptc_cntr}; + endcase + +// +// A match when RPTC_HRC is equal to RPTC_CNTR +// +assign hrc_match = rptc_ctrl[`PTC_RPTC_CTRL_EN] & (rptc_cntr == rptc_hrc); + +// +// A match when RPTC_LRC is equal to RPTC_CNTR +// +assign lrc_match = rptc_ctrl[`PTC_RPTC_CTRL_EN] & (rptc_cntr == rptc_lrc); + +// +// Restart counter when lrc_match asserted and RPTC_CTRL[SINGLE] cleared +// or when RPTC_CTRL[CNTRRST] is set +// +assign restart = lrc_match & ~rptc_ctrl[`PTC_RPTC_CTRL_SINGLE] + | rptc_ctrl[`PTC_RPTC_CTRL_CNTRRST]; + +// +// Stop counter when lrc_match and RPTC_CTRL[SINGLE] both asserted +// +assign stop = lrc_match & rptc_ctrl[`PTC_RPTC_CTRL_SINGLE]; + +// +// PWM reset when lrc_match or system reset +// +assign pwm_rst = lrc_match | wb_rst_i; + +// +// PWM output +// +always @(posedge wb_clk_i) // posedge pwm_rst or posedge hrc_match !!! Damjan + if (pwm_rst) + pwm_pad_o <= #1 1'b0; + else if (hrc_match) + pwm_pad_o <= #1 1'b1; + +// +// Generate an interrupt request +// +assign int_match = (lrc_match | hrc_match) & rptc_ctrl[`PTC_RPTC_CTRL_INTE]; + +// Register interrupt request +always @(posedge wb_rst_i or posedge wb_clk_i) // posedge int_match (instead of wb_rst_i) + if (wb_rst_i) + intr_reg <= #1 1'b0; + else if (int_match) + intr_reg <= #1 1'b1; + else + intr_reg <= #1 1'b0; + +// +// Alias +// +assign wb_inta_o = rptc_ctrl[`PTC_RPTC_CTRL_INT]; + +`else + +// +// When PTC is not implemented, drive all outputs as would when RPTC_CTRL +// is cleared and WISHBONE transfers complete with errors +// +assign wb_inta_o = 1'b0; +assign wb_ack_o = 1'b0; +assign wb_err_o = cyc_i & stb_i; +assign pwm_pad_o = 1'b0; +assign oen_padoen_o = 1'b1; + +// +// Read PTC registers +// +`ifdef PTC_READREGS +assign wb_dat_o = {dw{1'b0}}; +`endif + +`endif + +endmodule
diff --git a/verilog/rtl/rtc/hexmap.v b/verilog/rtl/rtc/hexmap.v new file mode 100644 index 0000000..15d4069 --- /dev/null +++ b/verilog/rtl/rtc/hexmap.v
@@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Filename: hexmap.v +// +// Project: A Real--time Clock Core +// +// Purpose: Converts a 4'bit hexadecimal value to the seven bits needed +// by a seven segment display, specifying which bits are on and +// which are off. +// +// The display I am working with, however, requires a separate +// controller. This file only provides part of the input for that +// controller. That controller deals with turning on each part +// of the display in a rotating fashion, since the hardware I have +// cannot display more than one character at a time. So, +// buyer beware--this is not a complete seven segment display +// solution. +// +// +// The outputs of this routine are numbered as follows: +// o_map[7] turns on the bar at the top of the display +// o_map[6] turns on the top of the '1' +// o_map[5] turns on the bottom of a '1' +// o_map[4] turns on the bar at the bottom of the display +// o_map[3] turns on the vertical bar at the bottom left +// o_map[2] turns on the vertical bar at the top left, and +// o_map[1] turns on the bar in the middle of the display. +// The dash if you will. +// Bit zero, from elsewhere, would be the decimal point. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Tecnology, LLC +// +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015, Gisselquist Technology, LLC +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +//// SPDX-License-Identifier: GPL-3.0-or-later +/////////////////////////////////////////////////////////////////////////// +module hexmap(i_clk, i_hex, o_map); + input i_clk; + input [3:0] i_hex; + output reg [7:1] o_map; + + always @(posedge i_clk) + case(i_hex) + 4'h0: o_map <= { 7'b1111110 }; + 4'h1: o_map <= { 7'b0110000 }; + 4'h2: o_map <= { 7'b1101101 }; + 4'h3: o_map <= { 7'b1111001 }; + 4'h4: o_map <= { 7'b0110011 }; + 4'h5: o_map <= { 7'b1011011 }; + 4'h6: o_map <= { 7'b1011111 }; + 4'h7: o_map <= { 7'b1110000 }; + 4'h8: o_map <= { 7'b1111111 }; + 4'h9: o_map <= { 7'b1111011 }; + 4'ha: o_map <= { 7'b1110111 }; + 4'hb: o_map <= { 7'b0011111 }; // b + 4'hc: o_map <= { 7'b1001110 }; + 4'hd: o_map <= { 7'b0111101 }; // d + 4'he: o_map <= { 7'b1001111 }; + 4'hf: o_map <= { 7'b1000111 }; + endcase +endmodule
diff --git a/verilog/rtl/rtc/rtcclock.v b/verilog/rtl/rtc/rtcclock.v new file mode 100644 index 0000000..31877db --- /dev/null +++ b/verilog/rtl/rtc/rtcclock.v
@@ -0,0 +1,504 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Filename: rtcclock.v +// +// Project: A Wishbone Controlled Real--time Clock Core +// +// Purpose: Implement a real time clock, including alarm, count--down +// timer, stopwatch, variable time frequency, and more. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015, Gisselquist Technology, LLC +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +//// SPDX-License-Identifier: GPL-3.0-or-later +/////////////////////////////////////////////////////////////////////////// +module rtcclock(i_clk, + // Wishbone interface + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, + // o_wb_ack, o_wb_stb, o_wb_data, // no reads here + // // Button inputs + // i_btn, + // Output registers + o_data, // multiplexed based upon i_wb_addr + // Output controls + o_sseg, o_led, o_interrupt, + // A once-per-day strobe on the last clock of the day + o_ppd, + // Time setting hack(s) + i_hack); + parameter DEFAULT_SPEED = 32'd2814750; //2af31e = 2^48 / 100e6 MHz + input i_clk; + input i_wb_cyc, i_wb_stb, i_wb_we; + input [2:0] i_wb_addr; + input [31:0] i_wb_data; + // input i_btn; + output reg [31:0] o_data; + output reg [31:0] o_sseg; + output wire [15:0] o_led; + output wire o_interrupt, o_ppd; + input i_hack; + + reg [31:0] stopwatch, ckspeed; + reg [25:0] clock, timer; + + wire ck_sel, tm_sel, sw_sel, sp_sel, al_sel; + assign ck_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b000)); + assign tm_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b001)); + assign sw_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b010)); + assign al_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b011)); + assign sp_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b100)); + + reg [39:0] ck_counter; + reg ck_carry; + always @(posedge i_clk) + { ck_carry, ck_counter } <= ck_counter + { 8'h00, ckspeed }; + + wire ck_pps; + reg ck_prepps, ck_ppm, ck_pph, ck_ppd; + reg [7:0] ck_sub; + initial clock = 26'h000000; + assign ck_pps = (ck_carry)&&(ck_prepps); + always @(posedge i_clk) + begin + if (ck_carry) + ck_sub <= ck_sub + 8'h1; + ck_prepps <= (ck_sub == 8'hff); + + if (ck_pps) + begin // advance the seconds + if (clock[3:0] >= 4'h9) + clock[3:0] <= 4'h0; + else + clock[3:0] <= clock[3:0] + 4'h1; + if (clock[7:0] >= 8'h59) + clock[7:4] <= 4'h0; + else if (clock[3:0] >= 4'h9) + clock[7:4] <= clock[7:4] + 4'h1; + end + ck_ppm <= (clock[7:0] == 8'h59); + + if ((ck_pps)&&(ck_ppm)) + begin // advance the minutes + if (clock[11:8] >= 4'h9) + clock[11:8] <= 4'h0; + else + clock[11:8] <= clock[11:8] + 4'h1; + if (clock[15:8] >= 8'h59) + clock[15:12] <= 4'h0; + else if (clock[11:8] >= 4'h9) + clock[15:12] <= clock[15:12] + 4'h1; + end + ck_pph <= (clock[15:0] == 16'h5959); + + if ((ck_pps)&&(ck_pph)) + begin // advance the hours + if (clock[21:16] >= 6'h23) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= 2'h0; + end else if (clock[19:16] >= 4'h9) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= clock[21:20] + 2'h1; + end else begin + clock[19:16] <= clock[19:16] + 4'h1; + end + end + ck_ppd <= (clock[21:0] == 22'h235959); + + + if ((ck_sel)&&(i_wb_we)) + begin + if (8'hff != i_wb_data[7:0]) + begin + clock[7:0] <= i_wb_data[7:0]; + ck_ppm <= (i_wb_data[7:0] == 8'h59); + end + if (8'hff != i_wb_data[15:8]) + begin + clock[15:8] <= i_wb_data[15:8]; + ck_pph <= (i_wb_data[15:8] == 8'h59); + end + if (6'h3f != i_wb_data[21:16]) + clock[21:16] <= i_wb_data[21:16]; + clock[25:22] <= i_wb_data[25:22]; + if (8'h00 == i_wb_data[7:0]) + ck_sub <= 8'h00; + end + end + + // Clock updates take several clocks, so let's make sure we + // are only looking at a valid clock value before testing it. + reg [21:0] ck_last_clock; + always @(posedge i_clk) + ck_last_clock <= clock[21:0]; + + + reg tm_pps, tm_ppm, tm_int; + wire tm_stopped, tm_running, tm_alarm; + assign tm_stopped = ~timer[24]; + assign tm_running = timer[24]; + assign tm_alarm = timer[25]; + reg [23:0] tm_start; + reg [7:0] tm_sub; + initial tm_start = 24'h00; + initial timer = 26'h00; + initial tm_int = 1'b0; + initial tm_pps = 1'b0; + always @(posedge i_clk) + begin + if (ck_carry) + begin + tm_sub <= tm_sub + 8'h1; + tm_pps <= (tm_sub == 8'hff); + end else + tm_pps <= 1'b0; + + if ((~tm_alarm)&&(tm_running)&&(tm_pps)) + begin // If we are running ... + timer[25] <= 1'b0; + if (timer[23:0] == 24'h00) + timer[25] <= 1'b1; + else if (timer[3:0] != 4'h0) + timer[3:0] <= timer[3:0]-4'h1; + else begin // last digit is a zero + timer[3:0] <= 4'h9; + if (timer[7:4] != 4'h0) + timer[7:4] <= timer[7:4]-4'h1; + else begin // last two digits are zero + timer[7:4] <= 4'h5; + if (timer[11:8] != 4'h0) + timer[11:8] <= timer[11:8]-4'h1; + else begin // last three digits are zero + timer[11:8] <= 4'h9; + if (timer[15:12] != 4'h0) + timer[15:12] <= timer[15:12]-4'h1; + else begin + timer[15:12] <= 4'h5; + if (timer[19:16] != 4'h0) + timer[19:16] <= timer[19:16]-4'h1; + else begin + // + timer[19:16] <= 4'h9; + timer[23:20] <= timer[23:20]-4'h1; + end + end + end + end + end + end + + if((~tm_alarm)&&(tm_running)) + begin + timer[25] <= (timer[23:0] == 24'h00); + tm_int <= (timer[23:0] == 24'h00); + end else tm_int <= 1'b0; + if (tm_alarm) + timer[24] <= 1'b0; + + if ((tm_sel)&&(i_wb_we)&&(tm_running)) // Writes while running + // Only allowed to stop the timer, nothing more + timer[24] <= i_wb_data[24]; + else if ((tm_sel)&&(i_wb_we)&&(tm_stopped)) // Writes while off + begin + timer[24] <= i_wb_data[24]; + if ((timer[24])||(i_wb_data[24])) + timer[25] <= 1'b0; + if (i_wb_data[23:0] != 24'h0000) + begin + timer[23:0] <= i_wb_data[23:0]; + tm_start <= i_wb_data[23:0]; + tm_sub <= 8'h00; + end else if (timer[23:0] == 24'h00) + begin // Resetting timer to last valid timer start val + timer[23:0] <= tm_start; + tm_sub <= 8'h00; + end + // Any write clears the alarm + timer[25] <= 1'b0; + end + end + + // + // Stopwatch functionality + // + // Setting bit '0' starts the stop watch, clearing it stops it. + // Writing to the register with bit '1' high will clear the stopwatch, + // and return it to zero provided that the stopwatch is stopped either + // before or after the write. Hence, writing a '2' to the device + // will always stop and clear it, whereas writing a '3' to the device + // will only clear it if it was already stopped. + reg sw_pps, sw_ppm, sw_pph; + reg [7:0] sw_sub; + wire sw_running; + assign sw_running = stopwatch[0]; + initial stopwatch = 32'h00000; + always @(posedge i_clk) + begin + sw_pps <= 1'b0; + if (sw_running) + begin + if (ck_carry) + begin + sw_sub <= sw_sub + 8'h1; + sw_pps <= (sw_sub == 8'hff); + end + end + + stopwatch[7:1] <= sw_sub[7:1]; + + if (sw_pps) + begin // Second hand + if (stopwatch[11:8] >= 4'h9) + stopwatch[11:8] <= 4'h0; + else + stopwatch[11:8] <= stopwatch[11:8] + 4'h1; + + if (stopwatch[15:8] >= 8'h59) + stopwatch[15:12] <= 4'h0; + else if (stopwatch[11:8] >= 4'h9) + stopwatch[15:12] <= stopwatch[15:12] + 4'h1; + sw_ppm <= (stopwatch[15:8] == 8'h59); + end else sw_ppm <= 1'b0; + + if (sw_ppm) + begin // Minutes + if (stopwatch[19:16] >= 4'h9) + stopwatch[19:16] <= 4'h0; + else + stopwatch[19:16] <= stopwatch[19:16]+4'h1; + + if (stopwatch[23:16] >= 8'h59) + stopwatch[23:20] <= 4'h0; + else if (stopwatch[19:16] >= 4'h9) + stopwatch[23:20] <= stopwatch[23:20]+4'h1; + sw_pph <= (stopwatch[23:16] == 8'h59); + end else sw_pph <= 1'b0; + + if (sw_pph) + begin // And hours + if (stopwatch[27:24] >= 4'h9) + stopwatch[27:24] <= 4'h0; + else + stopwatch[27:24] <= stopwatch[27:24]+4'h1; + + if((stopwatch[27:24] >= 4'h9)&&(stopwatch[31:28] < 4'hf)) + stopwatch[31:28] <= stopwatch[27:24]+4'h1; + end + + if ((sw_sel)&&(i_wb_we)) + begin + stopwatch[0] <= i_wb_data[0]; + if((i_wb_data[1])&&((~stopwatch[0])||(~i_wb_data[0]))) + begin + stopwatch[31:1] <= 31'h00; + sw_sub <= 8'h00; + sw_pps <= 1'b0; + sw_ppm <= 1'b0; + sw_pph <= 1'b0; + end + end + end + + // + // The alarm code + // + // Set the alarm register to the time you wish the board to "alarm". + // The "alarm" will take place once per day at that time. At that + // time, the RTC code will generate a clock interrupt, and the CPU/host + // can come and see that the alarm tripped. + // + // + reg [21:0] alarm_time; + reg al_int, // The alarm interrupt line + al_enabled, // Whether the alarm is enabled + al_tripped; // Whether the alarm has tripped + initial al_enabled= 1'b0; + initial al_tripped= 1'b0; + always @(posedge i_clk) + begin + if ((al_sel)&&(i_wb_we)) + begin + // Only adjust the alarm hours if the requested hours + // are valid. This allows writes to the register, + // without a prior read, to leave these configuration + // bits alone. + if (i_wb_data[21:16] != 6'h3f) + alarm_time[21:16] <= i_wb_data[21:16]; + // Here's the same thing for the minutes: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[15:8] != 8'hff) + alarm_time[15:8] <= i_wb_data[15:8]; + // Here's the same thing for the seconds: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[7:0] != 8'hff) + alarm_time[7:0] <= i_wb_data[7:0]; + al_enabled <= i_wb_data[24]; + // Reset the alarm if a '1' is written to the tripped + // register, or if the alarm is disabled. + if ((i_wb_data[25])||(~i_wb_data[24])) + al_tripped <= 1'b0; + end + + al_int <= 1'b0; + if ((ck_last_clock != alarm_time)&&(clock[21:0] == alarm_time) + &&(al_enabled)) + begin + al_tripped <= 1'b1; + al_int <= 1'b1; + end + end + + // + // The ckspeed register is equal to 2^48 divded by the number of + // clock ticks you expect per second. Adjust high for a slower + // clock, lower for a faster clock. In this fashion, a single + // real time clock RTL file can handle tracking the clock in any + // device. Further, because this is only the lower 32 bits of a + // 48 bit counter per seconds, the clock jitter is kept below + // 1 part in 65 thousand. + // + initial ckspeed = DEFAULT_SPEED; + // In the case of verilator, comment the above and uncomment the line + // below. The clock constant below is "close" to simulation time, + // meaning that my verilator simulation is running about 300x slower + // than board time. + // initial ckspeed = 32'd786432000; + always @(posedge i_clk) + if ((sp_sel)&&(i_wb_we)) + ckspeed <= i_wb_data; + + // + // If you want very fine precision control over your clock, you need + // to be able to transfer time from one location to another. This + // is the beginning of that means: by setting a wire, i_hack, high + // on a particular input, you can then read (later) what the clock + // time was on that input. + // + // What's missing from this high precision adjustment mechanism is a + // means of actually adjusting this time based upon the time + // difference you measure here between the hack time and some time + // on another clock, but we'll get there. + // + reg r_hack_carry; + reg [29:0] hack_time; + reg [39:0] hack_counter; + initial hack_time = 30'h0000; + initial hack_counter = 40'h0000; + always @(posedge i_clk) + if (i_hack) + begin + hack_time <= { clock[21:0], ck_sub }; + hack_counter <= ck_counter; + r_hack_carry <= ck_carry; + // if ck_carry is set, the clock register is in the + // middle of a two clock update. In that case .... + end else if (r_hack_carry) + begin // update again on the next clock to get the correct + // hack time. + hack_time <= { clock[21:0], ck_sub }; + r_hack_carry <= 1'b0; + end + + reg [15:0] h_sseg; + reg [3:1] dmask; + always @(posedge i_clk) + case(clock[25:24]) + 2'h1: begin h_sseg <= timer[15:0]; + if (tm_alarm) dmask <= 3'h7; + else begin + dmask[3] <= (12'h000 != timer[23:12]); // timer[15:12] + dmask[2] <= (16'h000 != timer[23: 8]); // timer[11: 8] + dmask[1] <= (20'h000 != timer[23: 4]); // timer[ 7: 4] + // dmask[0] <= 1'b1; // Always on + end end + 2'h2: begin h_sseg <= stopwatch[19:4]; + dmask[3] <= (12'h00 != stopwatch[27:16]); + dmask[2] <= (16'h000 != stopwatch[27:12]); + dmask[1] <= 1'b1; // Always on, stopwatch[11:8] + // dmask[0] <= 1'b1; // Always on, stopwatch[7:4] + end + 2'h3: begin h_sseg <= ck_last_clock[15:0]; + dmask[3:1] <= 3'h7; + end + default: begin // 4'h0 + h_sseg <= { 2'b00, ck_last_clock[21:8] }; + dmask[2:1] <= 2'b11; + dmask[3] <= (2'b00 != ck_last_clock[21:20]); + end + endcase + + wire [31:0] w_sseg; + assign w_sseg[ 0] = (~ck_sub[7]); + assign w_sseg[ 8] = (clock[25:24] == 2'h2); + assign w_sseg[16] = ((clock[25:24] == 2'h0)&&(~ck_sub[7]))||(clock[25:24] == 2'h3); + assign w_sseg[24] = 1'b0; + hexmap ha(i_clk, h_sseg[ 3: 0], w_sseg[ 7: 1]); + hexmap hb(i_clk, h_sseg[ 7: 4], w_sseg[15: 9]); + hexmap hc(i_clk, h_sseg[11: 8], w_sseg[23:17]); + hexmap hd(i_clk, h_sseg[15:12], w_sseg[31:25]); + + always @(posedge i_clk) + if ((tm_alarm || al_tripped)&&(ck_sub[7])) + o_sseg <= 32'h0000; + else + o_sseg <= { + (dmask[3])?w_sseg[31:24]:8'h00, + (dmask[2])?w_sseg[23:16]:8'h00, + (dmask[1])?w_sseg[15: 8]:8'h00, + w_sseg[ 7: 0] }; + + reg [17:0] ledreg; + always @(posedge i_clk) + if ((ck_pps)&&(ck_ppm)) + ledreg <= 18'h00; + else if (ck_carry) + ledreg <= ledreg + 18'h11; + assign o_led = (tm_alarm||al_tripped)?{ (16){ck_sub[7]}}: + { ledreg[17:10], + ledreg[10], ledreg[11], ledreg[12], ledreg[13], + ledreg[14], ledreg[15], ledreg[16], ledreg[17] }; + + assign o_interrupt = tm_int || al_int; + + // A once-per day strobe, on the last second of the day so that the + // the next clock is the first clock of the day. This is useful for + // connecting this module to a year/month/date date/calendar module. + assign o_ppd = (ck_ppd)&&(ck_pps); + + always @(posedge i_clk) + case(i_wb_addr[2:0]) + 3'b000: o_data <= { 6'h00, clock[25:22], ck_last_clock }; + 3'b001: o_data <= { 6'h00, timer }; + 3'b010: o_data <= stopwatch; + 3'b011: o_data <= { 6'h00, al_tripped, al_enabled, 2'b00, alarm_time }; + 3'b100: o_data <= ckspeed; + 3'b101: o_data <= { 2'b00, hack_time }; + 3'b110: o_data <= hack_counter[39:8]; + 3'b111: o_data <= { hack_counter[7:0], 24'h00 }; + endcase + +endmodule
diff --git a/verilog/rtl/rtc/rtcdate.v b/verilog/rtl/rtc/rtcdate.v new file mode 100644 index 0000000..b41f9ae --- /dev/null +++ b/verilog/rtl/rtc/rtcdate.v
@@ -0,0 +1,188 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Filename: rtcdate.v +// +// Project: A Wishbone Controlled Real--time Clock Core +// +// Purpose: +// This core provides a real-time date function that can be coupled with +// a real-time clock. The date provided is in Binary Coded Decimal (bcd) +// form, and available for reading and writing over the Wishbone Bus. +// +// WARNING: Race conditions exist when updating the date across the Wishbone +// bus at or near midnight. (This should be obvious, but it bears +// stating.) Specifically, if the update command shows up at the same +// clock as the ppd clock, then the ppd clock will be ignored and the +// new date will be the date of the day following midnight. However, +// if the update command shows up one clock before the ppd, then the date +// may be updated, but may have problems dealing with the last day of the +// month or year. To avoid race conditions, update the date sometime +// after the stroke of midnight and before 5 clocks before the next +// midnight. If you are concerned that you might hit a race condition, +// just read the clock again (5+ clocks later) to make certain you set +// it correctly. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015, Gisselquist Technology, LLC +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +//// SPDX-License-Identifier: GPL-3.0-or-later +/////////////////////////////////////////////////////////////////////////// +module rtcdate(i_clk, i_ppd, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, + o_wb_ack, o_wb_stall, o_wb_data); + input i_clk; + // A one part per day signal, i.e. basically a clock enable line that + // controls when the beginning of the day happens. This line should + // be high on the very last second of any day in order for the rtcdate + // module to always have the right date. + input i_ppd; + // Wishbone inputs + input i_wb_cyc, i_wb_stb, i_wb_we; + input [31:0] i_wb_data; + // Wishbone outputs + output reg o_wb_ack; + output wire o_wb_stall; + output wire [31:0] o_wb_data; + + + reg [5:0] r_day; + reg [4:0] r_mon; + reg [13:0] r_year; + + reg last_day_of_month, last_day_of_year, is_leap_year; + always @(posedge i_clk) + last_day_of_year <= (last_day_of_month) && (r_mon == 5'h12); + always @(posedge i_clk) + begin + case(r_mon) + 5'h01: last_day_of_month <= (r_day >= 6'h31); // Jan + 5'h02: last_day_of_month <= (r_day >= 6'h29) + ||((~is_leap_year)&&(r_day == 6'h28)); + 5'h03: last_day_of_month <= (r_day >= 6'h31); // March + 5'h04: last_day_of_month <= (r_day >= 6'h30); // April + 5'h05: last_day_of_month <= (r_day >= 6'h31); // May + 5'h06: last_day_of_month <= (r_day >= 6'h30); // June + 5'h07: last_day_of_month <= (r_day >= 6'h31); // July + 5'h08: last_day_of_month <= (r_day >= 6'h31); // August + 5'h09: last_day_of_month <= (r_day >= 6'h30); // Sept + 5'h10: last_day_of_month <= (r_day >= 6'h31); // October + 5'h11: last_day_of_month <= (r_day >= 6'h30); // November + 5'h12: last_day_of_month <= (r_day >= 6'h31); // December + default: last_day_of_month <= 1'b0; + endcase + end + + reg year_divisible_by_four, century_year, four_century_year; + always @(posedge i_clk) + year_divisible_by_four<= ((~r_year[0])&&(r_year[4]==r_year[1])); + always @(posedge i_clk) + century_year <= (r_year[7:0] == 8'h00); + always @(posedge i_clk) + four_century_year <= ((~r_year[8])&&((r_year[12]==r_year[9]))); + always @(posedge i_clk) + is_leap_year <= (year_divisible_by_four)&&((~century_year) + ||((century_year)&&(four_century_year))); + + + // Adjust the day of month + initial r_day = 6'h01; + always @(posedge i_clk) + begin + if ((r_day == 0)||(r_day > 6'h31)||(r_day[3:0] > 4'h9)) + r_day <= 6'h01; + else if ((i_ppd)&&(last_day_of_month)) + r_day <= 6'h01; + else if ((i_ppd)&&(r_day[3:0] != 4'h9)) + r_day[3:0] <= r_day[3:0] + 4'h1; + else if (i_ppd) + begin + r_day[3:0] <= 4'h0; + r_day[5:4] <= r_day[5:4] + 2'h1; + end + + if ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)&&(i_wb_data[7:0]!=8'hff)) + r_day <= i_wb_data[5:0]; + end + + // Adjust the month of the year + initial r_mon = 5'h01; + always @(posedge i_clk) + begin + if ((r_mon == 0)||(r_mon > 5'h12)||(r_mon[3:0] > 4'h9)) + r_mon <= 5'h01; + else if ((i_ppd)&&(last_day_of_year)) + r_mon <= 5'h01; + else if ((i_ppd)&&(last_day_of_month)&&(r_mon[3:0] != 4'h9)) + r_mon[3:0] <= r_mon[3:0] + 4'h1; + else if ((i_ppd)&&(last_day_of_month)) + begin + r_mon[3:0] <= 4'h0; + r_mon[4] <= 1; + end + + if ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)&&(i_wb_data[15:8]!=8'hff)) + r_mon <= i_wb_data[12:8]; + end + + // Adjust the year + initial r_year = 14'h2000; + always @(posedge i_clk) + begin + // Deal with any out of bounds conditions + if (r_year[3:0] > 4'h9) + r_year[3:0] <= 4'h0; + if (r_year[7:4] > 4'h9) + r_year[7:4] <= 4'h0; + if (r_year[11:8] > 4'h9) + r_year[11:8] <= 4'h0; + if ((i_ppd)&&(last_day_of_year)) + begin + if (r_year[3:0] != 4'h9) + r_year[3:0] <= r_year[3:0] + 4'h1; + else begin + r_year[3:0] <= 4'h0; + if (r_year[7:4] != 4'h9) + r_year[7:4] <= r_year[7:4] + 4'h1; + else begin + r_year[7:4] <= 4'h0; + if (r_year[11:8] != 4'h9) + r_year[11:8] <= r_year[11:8]+4'h1; + else begin + r_year[11:8] <= 4'h0; + r_year[13:12] <= r_year[13:12]+2'h1; + end + end + end + end + + if ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)&&(i_wb_data[31:16]!=16'hffff)) + r_year <= i_wb_data[29:16]; + end + + always @(posedge i_clk) + o_wb_ack <= ((i_wb_cyc)&&(i_wb_stb)); + assign o_wb_stall = 1'b0; + assign o_wb_data = { 2'h0, r_year, 3'h0, r_mon, 2'h0, r_day }; +endmodule
diff --git a/verilog/rtl/rtc/rtcgps.v b/verilog/rtl/rtc/rtcgps.v new file mode 100644 index 0000000..3769ffc --- /dev/null +++ b/verilog/rtl/rtc/rtcgps.v
@@ -0,0 +1,498 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Filename: rtcgps.v +// +// Project: A Wishbone Controlled Real--time Clock Core, w/ GPS synch +// +// Purpose: Implement a real time clock, including alarm, count--down +// timer, stopwatch, variable time frequency, and more. +// +// This particular version has hooks for a GPS 1PPS, as well as a +// finely tracked clock speed output, to allow for fine clock precision +// and good freewheeling even if/when GPS is lost. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015, Gisselquist Technology, LLC +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +//// SPDX-License-Identifier: GPL-3.0-or-later +/////////////////////////////////////////////////////////////////////////// +module rtcgps(i_clk, + // Wishbone interface + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, + // o_wb_ack, o_wb_stb, o_wb_data, // no reads here + // // Button inputs + // i_btn, + // Output registers + o_data, // multiplexed based upon i_wb_addr + // Output controls + o_sseg, o_led, o_interrupt, + // A once-per-day strobe on the last clock of the day + o_ppd, + // GPS interface + i_gps_valid, i_gps_pps, i_gps_ckspeed, + // Our personal timing, for debug purposes + o_rtc_pps); + parameter DEFAULT_SPEED = 32'd2814750; //2af31e = 2^48 / 100e6 MHz + input i_clk; + input i_wb_cyc, i_wb_stb, i_wb_we; + input [1:0] i_wb_addr; + input [31:0] i_wb_data; + // input i_btn; + output reg [31:0] o_data; + output reg [31:0] o_sseg; + output wire [15:0] o_led; + output wire o_interrupt, o_ppd; + // GPS interface + input i_gps_valid, i_gps_pps; + input [31:0] i_gps_ckspeed; + // Personal PPS + output wire o_rtc_pps; + + reg [23:0] clock; + reg [31:0] stopwatch, ckspeed; + reg [25:0] timer; + + wire ck_sel, tm_sel, sw_sel, al_sel; + assign ck_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr==2'b00)); + assign tm_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr==2'b01)); + assign sw_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr==2'b10)); + assign al_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr==2'b11)); + + reg [39:0] ck_counter; + reg ck_carry; + always @(posedge i_clk) + if ((i_gps_valid)&&(i_gps_pps)) + begin + ck_carry <= 0; + // Start our counter 2 clocks into the future. + // Why? Because if we hit the PPS, we'll be delayed + // one clock from true time. This (hopefully) locks + // us back onto true time. Further, if we end up + // off (i.e., go off before the GPS tick ...) then + // the GPS tick will put us back on track ... likewise + // we've got code following that should keep us from + // ever producing two PPS's per second. + ck_counter <= { 7'h00, ckspeed, 1'b0 }; + end else + { ck_carry, ck_counter }<=ck_counter+{ 8'h00, ckspeed }; + + reg ck_pps; + reg ck_ppm, ck_pph, ck_ppd; + reg [7:0] ck_sub; + initial clock = 24'h00000000; + always @(posedge i_clk) + if ((i_gps_pps)&&(i_gps_valid)&&(ck_sub[7])) + ck_pps <= 1'b1; + else if ((ck_carry)&&(ck_sub == 8'hff)) + ck_pps <= 1'b1; + else + ck_pps <= 1'b0; + + assign o_rtc_pps = ck_pps; + always @(posedge i_clk) + begin + if ((i_gps_valid)&&(i_gps_pps)) + ck_sub <= 0; + else if (ck_carry) + ck_sub <= ck_sub + 1; + + if (ck_pps) + begin // advance the seconds + if (clock[3:0] >= 4'h9) + clock[3:0] <= 4'h0; + else + clock[3:0] <= clock[3:0] + 4'h1; + if (clock[7:0] >= 8'h59) + clock[7:4] <= 4'h0; + else if (clock[3:0] >= 4'h9) + clock[7:4] <= clock[7:4] + 4'h1; + end + ck_ppm <= (clock[7:0] == 8'h59); + + if ((ck_pps)&&(ck_ppm)) + begin // advance the minutes + if (clock[11:8] >= 4'h9) + clock[11:8] <= 4'h0; + else + clock[11:8] <= clock[11:8] + 4'h1; + if (clock[15:8] >= 8'h59) + clock[15:12] <= 4'h0; + else if (clock[11:8] >= 4'h9) + clock[15:12] <= clock[15:12] + 4'h1; + end + ck_pph <= (clock[15:0] == 16'h5959); + + if ((ck_pps)&&(ck_pph)) + begin // advance the hours + if (clock[21:16] >= 6'h23) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= 2'h0; + end else if (clock[19:16] >= 4'h9) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= clock[21:20] + 2'h1; + end else begin + clock[19:16] <= clock[19:16] + 4'h1; + end + end + ck_ppd <= (clock[21:0] == 22'h235959); + + + if ((ck_sel)&&(i_wb_we)) + begin + if (8'hff != i_wb_data[7:0]) + begin + clock[7:0] <= i_wb_data[7:0]; + ck_ppm <= (i_wb_data[7:0] == 8'h59); + end + if (8'hff != i_wb_data[15:8]) + begin + clock[15:8] <= i_wb_data[15:8]; + ck_pph <= (i_wb_data[15:8] == 8'h59); + end + if (6'h3f != i_wb_data[21:16]) + clock[21:16] <= i_wb_data[21:16]; + clock[23:22] <= i_wb_data[25:24]; + if ((~i_gps_valid)&&(8'h00 == i_wb_data[7:0])) + ck_sub <= 8'h00; + end + end + + reg [21:0] ck_last_clock; + always @(posedge i_clk) + ck_last_clock <= clock[21:0]; + + reg tm_pps, tm_ppm, tm_int; + wire tm_stopped, tm_running, tm_alarm; + assign tm_stopped = ~timer[24]; + assign tm_running = timer[24]; + assign tm_alarm = timer[25]; + reg [23:0] tm_start; + reg [7:0] tm_sub; + initial tm_start = 24'h00; + initial timer = 26'h00; + initial tm_int = 1'b0; + initial tm_pps = 1'b0; + always @(posedge i_clk) + begin + if (ck_carry) + begin + tm_sub <= tm_sub + 1; + tm_pps <= (tm_sub == 8'hff); + end else + tm_pps <= 1'b0; + + if ((~tm_alarm)&&(tm_running)&&(tm_pps)) + begin // If we are running ... + timer[25] <= 1'b0; + if (timer[23:0] == 24'h00) + timer[25] <= 1'b1; + else if (timer[3:0] != 4'h0) + timer[3:0] <= timer[3:0]-4'h1; + else begin // last digit is a zero + timer[3:0] <= 4'h9; + if (timer[7:4] != 4'h0) + timer[7:4] <= timer[7:4]-4'h1; + else begin // last two digits are zero + timer[7:4] <= 4'h5; + if (timer[11:8] != 4'h0) + timer[11:8] <= timer[11:8]-4'h1; + else begin // last three digits are zero + timer[11:8] <= 4'h9; + if (timer[15:12] != 4'h0) + timer[15:12] <= timer[15:12]-4'h1; + else begin + timer[15:12] <= 4'h5; + if (timer[19:16] != 4'h0) + timer[19:16] <= timer[19:16]-4'h1; + else begin + // + timer[19:16] <= 4'h9; + timer[23:20] <= timer[23:20]-4'h1; + end + end + end + end + end + end + + if((~tm_alarm)&&(tm_running)) + begin + timer[25] <= (timer[23:0] == 24'h00); + tm_int <= (timer[23:0] == 24'h00); + end else tm_int <= 1'b0; + if (tm_alarm) + timer[24] <= 1'b0; + + if ((tm_sel)&&(i_wb_we)&&(tm_running)) // Writes while running + // Only allowed to stop the timer, nothing more + timer[24] <= i_wb_data[24]; + else if ((tm_sel)&&(i_wb_we)&&(tm_stopped)) // Writes while off + begin + timer[24] <= i_wb_data[24]; + if ((timer[24])||(i_wb_data[24])) + timer[25] <= 1'b0; + if (i_wb_data[23:0] != 24'h0000) + begin + timer[23:0] <= i_wb_data[23:0]; + tm_start <= i_wb_data[23:0]; + tm_sub <= 8'h00; + end else if (timer[23:0] == 24'h00) + begin // Resetting timer to last valid timer start val + timer[23:0] <= tm_start; + tm_sub <= 8'h00; + end + // Any write clears the alarm + timer[25] <= 1'b0; + end + end + + // + // Stopwatch functionality + // + // Setting bit '0' starts the stop watch, clearing it stops it. + // Writing to the register with bit '1' high will clear the stopwatch, + // and return it to zero provided that the stopwatch is stopped either + // before or after the write. Hence, writing a '2' to the device + // will always stop and clear it, whereas writing a '3' to the device + // will only clear it if it was already stopped. + reg sw_pps, sw_ppm, sw_pph; + reg [7:0] sw_sub; + wire sw_running; + assign sw_running = stopwatch[0]; + initial stopwatch = 32'h00000; + always @(posedge i_clk) + begin + sw_pps <= 1'b0; + if (sw_running) + begin + if (ck_carry) + begin + sw_sub <= sw_sub + 1; + sw_pps <= (sw_sub == 8'hff); + end + end + + stopwatch[7:1] <= sw_sub[7:1]; + + if (sw_pps) + begin // Second hand + if (stopwatch[11:8] >= 4'h9) + stopwatch[11:8] <= 4'h0; + else + stopwatch[11:8] <= stopwatch[11:8] + 4'h1; + + if (stopwatch[15:8] >= 8'h59) + stopwatch[15:12] <= 4'h0; + else if (stopwatch[11:8] >= 4'h9) + stopwatch[15:12] <= stopwatch[15:12] + 4'h1; + sw_ppm <= (stopwatch[15:8] == 8'h59); + end else sw_ppm <= 1'b0; + + if (sw_ppm) + begin // Minutes + if (stopwatch[19:16] >= 4'h9) + stopwatch[19:16] <= 4'h0; + else + stopwatch[19:16] <= stopwatch[19:16]+4'h1; + + if (stopwatch[23:16] >= 8'h59) + stopwatch[23:20] <= 4'h0; + else if (stopwatch[19:16] >= 4'h9) + stopwatch[23:20] <= stopwatch[23:20]+4'h1; + sw_pph <= (stopwatch[23:16] == 8'h59); + end else sw_pph <= 1'b0; + + if (sw_pph) + begin // And hours + if (stopwatch[27:24] >= 4'h9) + stopwatch[27:24] <= 4'h0; + else + stopwatch[27:24] <= stopwatch[27:24]+4'h1; + + if((stopwatch[27:24] >= 4'h9)&&(stopwatch[31:28] < 4'hf)) + stopwatch[31:28] <= stopwatch[27:24]+4'h1; + end + + if ((sw_sel)&&(i_wb_we)) + begin + stopwatch[0] <= i_wb_data[0]; + if((i_wb_data[1])&&((~stopwatch[0])||(~i_wb_data[0]))) + begin + stopwatch[31:1] <= 31'h00; + sw_sub <= 8'h00; + sw_pps <= 1'b0; + sw_ppm <= 1'b0; + sw_pph <= 1'b0; + end + end + end + + // + // The alarm code + // + // Set the alarm register to the time you wish the board to "alarm". + // The "alarm" will take place once per day at that time. At that + // time, the RTC code will generate a clock interrupt, and the CPU/host + // can come and see that the alarm tripped. + // + // + reg [21:0] alarm_time; + reg al_int, // The alarm interrupt line + al_enabled, // Whether the alarm is enabled + al_tripped; // Whether the alarm has tripped + initial al_enabled= 1'b0; + initial al_tripped= 1'b0; + always @(posedge i_clk) + begin + if ((al_sel)&&(i_wb_we)) + begin + // Only adjust the alarm hours if the requested hours + // are valid. This allows writes to the register, + // without a prior read, to leave these configuration + // bits alone. + if (i_wb_data[21:16] != 6'h3f) + alarm_time[21:16] <= i_wb_data[21:16]; + // Here's the same thing for the minutes: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[15:8] != 8'hff) + alarm_time[15:8] <= i_wb_data[15:8]; + // Here's the same thing for the seconds: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[7:0] != 8'hff) + alarm_time[7:0] <= i_wb_data[7:0]; + al_enabled <= i_wb_data[24]; + // Reset the alarm if a '1' is written to the tripped + // register, or if the alarm is disabled. + if ((i_wb_data[25])||(~i_wb_data[24])) + al_tripped <= 1'b0; + end + + al_int <= 1'b0; + if ((ck_last_clock != alarm_time)&&(clock[21:0] == alarm_time)&&(al_enabled)) + begin + al_tripped <= 1'b1; + al_int <= 1'b1; + end + end + + // + // The ckspeed register is equal to 2^48 divded by the number of + // clock ticks you expect per second. Adjust high for a slower + // clock, lower for a faster clock. In this fashion, a single + // real time clock RTL file can handle tracking the clock in any + // device. Further, because this is only the lower 32 bits of a + // 48 bit counter per seconds, the clock jitter is kept below + // 1 part in 65 thousand. + // + initial ckspeed = DEFAULT_SPEED; + // In the case of verilator, comment the above and uncomment the line + // below. The clock constant below is "close" to simulation time, + // meaning that my verilator simulation is running about 300x slower + // than board time. + // initial ckspeed = 32'd786432000; + always @(posedge i_clk) + if (i_gps_valid) + ckspeed <= i_gps_ckspeed; + + reg [15:0] h_sseg; + reg [3:1] dmask; + always @(posedge i_clk) + case(clock[23:22]) + 2'h1: begin h_sseg <= timer[15:0]; + if (tm_alarm) dmask <= 3'h7; + else begin + dmask[3] <= (12'h000 != timer[23:12]); // timer[15:12] + dmask[2] <= (16'h000 != timer[23: 8]); // timer[11: 8] + dmask[1] <= (20'h000 != timer[23: 4]); // timer[ 7: 4] + // dmask[0] <= 1'b1; // Always on + end end + 2'h2: begin h_sseg <= stopwatch[19:4]; + dmask[3] <= (12'h00 != stopwatch[27:16]); + dmask[2] <= (16'h000 != stopwatch[27:12]); + dmask[1] <= 1'b1; // Always on, stopwatch[11:8] + // dmask[0] <= 1'b1; // Always on, stopwatch[7:4] + end + 2'h3: begin h_sseg <= clock[15:0]; + dmask[3:1] <= 3'h7; + end + default: begin // 4'h0 + h_sseg <= { 2'b00, clock[21:8] }; + dmask[2:1] <= 2'b11; + dmask[3] <= (2'b00 != clock[21:20]); + end + endcase + + wire [31:0] w_sseg; + assign w_sseg[ 0] = (i_gps_valid)?(ck_sub[7:5]==3'h0):(~ck_sub[0]); + assign w_sseg[ 8] = (i_gps_valid)?(ck_sub[7:5]==3'h0):(~ck_sub[0]); + assign w_sseg[16] = (i_gps_valid)?(ck_sub[7:5]==3'h0):(~ck_sub[0]); + // assign w_sseg[ 8] = w_sseg[0]; + // assign w_sseg[16] = w_sseg[0]; + assign w_sseg[24] = 1'b0; + hexmap ha(i_clk, h_sseg[ 3: 0], w_sseg[ 7: 1]); + hexmap hb(i_clk, h_sseg[ 7: 4], w_sseg[15: 9]); + hexmap hc(i_clk, h_sseg[11: 8], w_sseg[23:17]); + hexmap hd(i_clk, h_sseg[15:12], w_sseg[31:25]); + + always @(posedge i_clk) + if ((tm_alarm || al_tripped)&&(ck_sub[7])) + o_sseg <= 32'h0000; + else + o_sseg <= { + (dmask[3])?w_sseg[31:24]:8'h00, + (dmask[2])?w_sseg[23:16]:8'h00, + (dmask[1])?w_sseg[15: 8]:8'h00, + w_sseg[ 7: 0] }; + + reg [17:0] ledreg; + always @(posedge i_clk) + if ((ck_pps)&&(ck_ppm)) + ledreg <= 18'h00; + else if (ck_carry) + ledreg <= ledreg + 18'h11; + assign o_led = (tm_alarm||al_tripped)?{ (16){ck_sub[7]}}: + { ledreg[17:10], + ledreg[10], ledreg[11], ledreg[12], ledreg[13], + ledreg[14], ledreg[15], ledreg[16], ledreg[17] }; + + assign o_interrupt = tm_int || al_int; + + // A once-per day strobe, on the last second of the day so that the + // the next clock is the first clock of the day. This is useful for + // connecting this module to a year/month/date date/calendar module. + assign o_ppd = (ck_ppd)&&(ck_pps); + + always @(posedge i_clk) + case(i_wb_addr) + 2'b00: o_data <= { ~i_gps_valid, 5'h0, clock[23:22], 2'b00, clock[21:0] }; + 2'b01: o_data <= { 6'h00, timer }; + 2'b10: o_data <= stopwatch; + 2'b11: o_data <= { 6'h00, al_tripped, al_enabled, 2'b00, alarm_time }; + endcase + +endmodule
diff --git a/verilog/rtl/rtc/rtclight.v b/verilog/rtl/rtc/rtclight.v new file mode 100644 index 0000000..2bcb669 --- /dev/null +++ b/verilog/rtl/rtc/rtclight.v
@@ -0,0 +1,413 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Filename: rtclight.v +// +// Project: A Wishbone Controlled Real--time Clock Core +// +// Purpose: Implement a real time clock, including alarm, count--down +// timer, stopwatch, variable time frequency, and more. +// +// This is a light-weight version of the RTC found in this directory. +// Unlike the full RTC, this version does not support time hacks, seven +// segment display outputs, or LED's. It is an RTC for an internal core +// only. (That's how I was using it on one of my projects anyway ...) +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015, Gisselquist Technology, LLC +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +//// SPDX-License-Identifier: GPL-3.0-or-later +/////////////////////////////////////////////////////////////////////////// +module rtclight(i_clk, + // Wishbone interface + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, + // o_wb_ack, o_wb_stb, o_wb_data, // no reads here + // // Button inputs + // i_btn, + // Output registers + o_data, // multiplexed based upon i_wb_addr + // Output controls + o_interrupt, + // A once-per-day strobe on the last clock of the day + o_ppd); + parameter DEFAULT_SPEED = 32'd2814750; // 100 Mhz + input i_clk; + input i_wb_cyc, i_wb_stb, i_wb_we; + input [2:0] i_wb_addr; + input [31:0] i_wb_data; + // input i_btn; + output reg [31:0] o_data; + output wire o_interrupt, o_ppd; + + reg [21:0] clock; + reg [31:0] stopwatch, ckspeed; + reg [25:0] timer; + + wire ck_sel, tm_sel, sw_sel, sp_sel, al_sel; + assign ck_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b000)); + assign tm_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b001)); + assign sw_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b010)); + assign al_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b011)); + assign sp_sel = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_addr[2:0]==3'b100)); + + reg ck_carry; + reg [39:0] ck_counter; + initial ck_carry = 1'b0; + initial ck_counter = 40'h00; + always @(posedge i_clk) + { ck_carry, ck_counter } <= ck_counter + { 8'h00, ckspeed }; + + wire ck_pps; + reg ck_prepps, ck_ppm, ck_pph, ck_ppd; + reg [7:0] ck_sub; + initial clock = 22'h00000; + assign ck_pps = (ck_carry)&&(ck_prepps); + always @(posedge i_clk) + begin + if (ck_carry) + ck_sub <= ck_sub + 8'h1; + ck_prepps <= (ck_sub == 8'hff); + + if (ck_pps) + begin // advance the seconds + if (clock[3:0] >= 4'h9) + clock[3:0] <= 4'h0; + else + clock[3:0] <= clock[3:0] + 4'h1; + if (clock[7:0] >= 8'h59) + clock[7:4] <= 4'h0; + else if (clock[3:0] >= 4'h9) + clock[7:4] <= clock[7:4] + 4'h1; + end + ck_ppm <= (clock[7:0] == 8'h59); + + if ((ck_pps)&&(ck_ppm)) + begin // advance the minutes + if (clock[11:8] >= 4'h9) + clock[11:8] <= 4'h0; + else + clock[11:8] <= clock[11:8] + 4'h1; + if (clock[15:8] >= 8'h59) + clock[15:12] <= 4'h0; + else if (clock[11:8] >= 4'h9) + clock[15:12] <= clock[15:12] + 4'h1; + end + ck_pph <= (clock[15:0] == 16'h5959); + + if ((ck_pps)&&(ck_pph)) + begin // advance the hours + if (clock[21:16] >= 6'h23) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= 2'h0; + end else if (clock[19:16] >= 4'h9) + begin + clock[19:16] <= 4'h0; + clock[21:20] <= clock[21:20] + 2'h1; + end else begin + clock[19:16] <= clock[19:16] + 4'h1; + end + end + ck_ppd <= (clock[21:0] == 22'h235959); + + + if ((ck_sel)&&(i_wb_we)) + begin + if (8'hff != i_wb_data[7:0]) + begin + clock[7:0] <= i_wb_data[7:0]; + ck_ppm <= (i_wb_data[7:0] == 8'h59); + end + if (8'hff != i_wb_data[15:8]) + begin + clock[15:8] <= i_wb_data[15:8]; + ck_pph <= (i_wb_data[15:8] == 8'h59); + end + if (6'h3f != i_wb_data[21:16]) + clock[21:16] <= i_wb_data[21:16]; + if (8'h00 == i_wb_data[7:0]) + ck_sub <= 8'h00; + end + end + + // Clock updates take several clocks, so let's make sure we + // are only looking at a valid clock value before testing it. + reg [21:0] ck_last_clock; + always @(posedge i_clk) + ck_last_clock <= clock[21:0]; + + + reg tm_pps, tm_ppm, tm_int; + wire tm_stopped, tm_running, tm_alarm; + assign tm_stopped = ~timer[24]; + assign tm_running = timer[24]; + assign tm_alarm = timer[25]; + reg [23:0] tm_start; + reg [7:0] tm_sub; + initial tm_start = 24'h00; + initial timer = 26'h00; + initial tm_int = 1'b0; + initial tm_pps = 1'b0; + always @(posedge i_clk) + begin + if (ck_carry) + begin + tm_sub <= tm_sub + 8'h1; + tm_pps <= (tm_sub == 8'hff); + end else + tm_pps <= 1'b0; + + if ((~tm_alarm)&&(tm_running)&&(tm_pps)) + begin // If we are running ... + timer[25] <= 1'b0; + if (timer[23:0] == 24'h00) + timer[25] <= 1'b1; + else if (timer[3:0] != 4'h0) + timer[3:0] <= timer[3:0]-4'h1; + else begin // last digit is a zero + timer[3:0] <= 4'h9; + if (timer[7:4] != 4'h0) + timer[7:4] <= timer[7:4]-4'h1; + else begin // last two digits are zero + timer[7:4] <= 4'h5; + if (timer[11:8] != 4'h0) + timer[11:8] <= timer[11:8]-4'h1; + else begin // last three digits are zero + timer[11:8] <= 4'h9; + if (timer[15:12] != 4'h0) + timer[15:12] <= timer[15:12]-4'h1; + else begin + timer[15:12] <= 4'h5; + if (timer[19:16] != 4'h0) + timer[19:16] <= timer[19:16]-4'h1; + else begin + // + timer[19:16] <= 4'h9; + timer[23:20] <= timer[23:20]-4'h1; + end + end + end + end + end + end + + if((~tm_alarm)&&(tm_running)) + begin + timer[25] <= (timer[23:0] == 24'h00); + tm_int <= (timer[23:0] == 24'h00); + end else tm_int <= 1'b0; + if (tm_alarm) + timer[24] <= 1'b0; + + if ((tm_sel)&&(i_wb_we)&&(tm_running)) // Writes while running + // Only allowed to stop the timer, nothing more + timer[24] <= i_wb_data[24]; + else if ((tm_sel)&&(i_wb_we)&&(tm_stopped)) // Writes while off + begin + timer[24] <= i_wb_data[24]; + if ((timer[24])||(i_wb_data[24])) + timer[25] <= 1'b0; + if (i_wb_data[23:0] != 24'h0000) + begin + timer[23:0] <= i_wb_data[23:0]; + tm_start <= i_wb_data[23:0]; + tm_sub <= 8'h00; + end else if (timer[23:0] == 24'h00) + begin // Resetting timer to last valid timer start val + timer[23:0] <= tm_start; + tm_sub <= 8'h00; + end + // Any write clears the alarm + timer[25] <= 1'b0; + end + end + + // + // Stopwatch functionality + // + // Setting bit '0' starts the stop watch, clearing it stops it. + // Writing to the register with bit '1' high will clear the stopwatch, + // and return it to zero provided that the stopwatch is stopped either + // before or after the write. Hence, writing a '2' to the device + // will always stop and clear it, whereas writing a '3' to the device + // will only clear it if it was already stopped. + reg sw_pps, sw_ppm, sw_pph; + reg [7:0] sw_sub; + wire sw_running; + assign sw_running = stopwatch[0]; + initial stopwatch = 32'h00000; + always @(posedge i_clk) + begin + sw_pps <= 1'b0; + if (sw_running) + begin + if (ck_carry) + begin + sw_sub <= sw_sub + 8'h1; + sw_pps <= (sw_sub == 8'hff); + end + end + + stopwatch[7:1] <= sw_sub[7:1]; + + if (sw_pps) + begin // Second hand + if (stopwatch[11:8] >= 4'h9) + stopwatch[11:8] <= 4'h0; + else + stopwatch[11:8] <= stopwatch[11:8] + 4'h1; + + if (stopwatch[15:8] >= 8'h59) + stopwatch[15:12] <= 4'h0; + else if (stopwatch[11:8] >= 4'h9) + stopwatch[15:12] <= stopwatch[15:12] + 4'h1; + sw_ppm <= (stopwatch[15:8] == 8'h59); + end else sw_ppm <= 1'b0; + + if (sw_ppm) + begin // Minutes + if (stopwatch[19:16] >= 4'h9) + stopwatch[19:16] <= 4'h0; + else + stopwatch[19:16] <= stopwatch[19:16]+4'h1; + + if (stopwatch[23:16] >= 8'h59) + stopwatch[23:20] <= 4'h0; + else if (stopwatch[19:16] >= 4'h9) + stopwatch[23:20] <= stopwatch[23:20]+4'h1; + sw_pph <= (stopwatch[23:16] == 8'h59); + end else sw_pph <= 1'b0; + + if (sw_pph) + begin // And hours + if (stopwatch[27:24] >= 4'h9) + stopwatch[27:24] <= 4'h0; + else + stopwatch[27:24] <= stopwatch[27:24]+4'h1; + + if((stopwatch[27:24] >= 4'h9)&&(stopwatch[31:28] < 4'hf)) + stopwatch[31:28] <= stopwatch[27:24]+4'h1; + end + + if ((sw_sel)&&(i_wb_we)) + begin + stopwatch[0] <= i_wb_data[0]; + if((i_wb_data[1])&&((~stopwatch[0])||(~i_wb_data[0]))) + begin + stopwatch[31:1] <= 31'h00; + sw_sub <= 8'h00; + sw_pps <= 1'b0; + sw_ppm <= 1'b0; + sw_pph <= 1'b0; + end + end + end + + // + // The alarm code + // + // Set the alarm register to the time you wish the board to "alarm". + // The "alarm" will take place once per day at that time. At that + // time, the RTC code will generate a clock interrupt, and the CPU/host + // can come and see that the alarm tripped. + // + // + reg [21:0] alarm_time; + reg al_int, // The alarm interrupt line + al_enabled, // Whether the alarm is enabled + al_tripped; // Whether the alarm has tripped + initial al_enabled= 1'b0; + initial al_tripped= 1'b0; + always @(posedge i_clk) + begin + if ((al_sel)&&(i_wb_we)) + begin + // Only adjust the alarm hours if the requested hours + // are valid. This allows writes to the register, + // without a prior read, to leave these configuration + // bits alone. + if (i_wb_data[21:16] != 6'h3f) + alarm_time[21:16] <= i_wb_data[21:16]; + // Here's the same thing for the minutes: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[15:8] != 8'hff) + alarm_time[15:8] <= i_wb_data[15:8]; + // Here's the same thing for the seconds: only adjust + // the alarm minutes if the new bits are not all 1's. + if (i_wb_data[7:0] != 8'hff) + alarm_time[7:0] <= i_wb_data[7:0]; + al_enabled <= i_wb_data[24]; + // Reset the alarm if a '1' is written to the tripped + // register, or if the alarm is disabled. + if ((i_wb_data[25])||(~i_wb_data[24])) + al_tripped <= 1'b0; + end + + al_int <= 1'b0; + if ((ck_last_clock != alarm_time)&&(clock[21:0] == alarm_time) + &&(al_enabled)) + begin + al_tripped <= 1'b1; + al_int <= 1'b1; + end + end + + // + // The ckspeed register is equal to 2^48 divded by the number of + // clock ticks you expect per second. Adjust high for a slower + // clock, lower for a faster clock. In this fashion, a single + // real time clock RTL file can handle tracking the clock in any + // device. Further, because this is only the lower 32 bits of a + // 48 bit counter per seconds, the clock jitter is kept below + // 1 part in 65 thousand. + // + initial ckspeed = DEFAULT_SPEED; // 2af31e = 2^48 / 100e6 MHz + // In the case of verilator, comment the above and uncomment the line + // below. The clock constant below is "close" to simulation time, + // meaning that my verilator simulation is running about 300x slower + // than board time. + // initial ckspeed = 32'd786432000; + always @(posedge i_clk) + if ((sp_sel)&&(i_wb_we)) + ckspeed <= i_wb_data; + + assign o_interrupt = tm_int || al_int; + + // A once-per day strobe, on the last second of the day so that the + // the next clock is the first clock of the day. This is useful for + // connecting this module to a year/month/date date/calendar module. + assign o_ppd = (ck_ppd)&&(ck_pps); + + always @(posedge i_clk) + case(i_wb_addr[2:0]) + 3'b000: o_data <= { 10'h0, ck_last_clock }; + 3'b001: o_data <= { 6'h00, timer }; + 3'b010: o_data <= stopwatch; + 3'b011: o_data <= { 6'h00, al_tripped, al_enabled, 2'b00, alarm_time }; + 3'b100: o_data <= ckspeed; + default: o_data <= 32'h000; + endcase + +endmodule
diff --git a/verilog/rtl/user_proj_example.v b/verilog/rtl/user_proj_example.v index b33e032..7996cdc 100644 --- a/verilog/rtl/user_proj_example.v +++ b/verilog/rtl/user_proj_example.v
@@ -1,5 +1,3 @@ -// SPDX-FileCopyrightText: 2020 Efabless Corporation -// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,6 +12,7 @@ // SPDX-License-Identifier: Apache-2.0 `default_nettype none + /* *------------------------------------------------------------- * @@ -58,8 +57,8 @@ input [3:0] wbs_sel_i, input [31:0] wbs_dat_i, input [31:0] wbs_adr_i, - output wbs_ack_o, - output [31:0] wbs_dat_o, + output reg wbs_ack_o, + output reg [31:0] wbs_dat_o, // Logic Analyzer Signals input [127:0] la_data_in, @@ -76,11 +75,9 @@ ); wire clk; wire rst; - wire [`MPRJ_IO_PADS-1:0] io_in; wire [`MPRJ_IO_PADS-1:0] io_out; wire [`MPRJ_IO_PADS-1:0] io_oeb; - wire [31:0] rdata; wire [31:0] wdata; wire [BITS-1:0] count; @@ -89,83 +86,212 @@ wire [3:0] wstrb; wire [31:0] la_write; - // WB MI A - assign valid = wbs_cyc_i && wbs_stb_i; - assign wstrb = wbs_sel_i & {4{wbs_we_i}}; - assign wbs_dat_o = rdata; - assign wdata = wbs_dat_i; + wire pwm1_select , pwm2_select , i2c_select , rtc_select , pid_select ; + wire pwm1_wbs_stb_i , pwm2_wbs_stb_i , i2c_wbs_stb_i , rtc_wbs_stb_i , pid_wbs_stb_i ; + wire pwm1_wbs_ack_o , pwm2_wbs_ack_o , i2c_wbs_ack_o , rtc_wbs_ack_o , pid_wbs_ack_o ; - // IO - assign io_out = count; - assign io_oeb = {(`MPRJ_IO_PADS-1){rst}}; + wire [31:0] pwm1_wbs_dat_o ; + wire [31:0] pwm2_wbs_dat_o ; + wire [7:0] i2c_wbs_dat_o ; + wire [31:0] rtc_wbs_dat_o ; + wire [31:0] pid_wbs_dat_o ; + + wire pwm_out1 , pwm_out2 ; + wire pwm_out1_oen , pwm_out2_oen ; + wire ptc1_intr , ptc2_intr , i2c_intr , rtc_intr ; + reg led1, led2, led3 ; + wire ptc_clk1,ptc_clk2; + wire capt_in1,capt_in2; + wire i2c_scl_in, i2c_sda_in, scl_pad_o, scl_padoen_o, sda_pad_o, sda_padoen_o ; + + assign ptc_clk1 = io_in[0] ; // IO[0] + assign io_oeb[0]= 1'b1; + + assign ptc_clk2 = io_in[1] ; // IO[1] + assign io_oeb[1]= 1'b1; + + assign capt_in1 = io_in[2] ; // IO[2] + assign io_oeb[1]= 1'b1; + + assign capt_in2 = io_in[3] ; // IO[3] + assign io_oeb[1]= 1'b1; + + + assign io_out[5:4] = la_data_in[110:109] ; // IO[5:4] + assign io_oeb[5:4] = la_oenb[110:109] ; + + assign io_out[7:6] = {pwm_out2,pwm_out1} ; // IO[7:6] + assign io_oeb[7:6] = {pwm_out2_oen,pwm_out1_oen} ; + + assign io_out[8] = scl_pad_o ; // IO[8] + assign io_oeb[8] = scl_padoen_o ; + + assign i2c_scl_in = io_in[9] ; // IO[9] + assign io_oeb[9]= 1'b1; + + assign io_out[10] = sda_pad_o ; // IO[10] + assign io_oeb[10] = sda_padoen_o; + + assign i2c_sda_in = io_in[11] ; // IO[11] + assign io_oeb[11]= 1'b1; + + // Inputs // IRQ - assign irq = 3'b000; // Unused + assign irq = {rtc_intr , i2c_intr , {ptc2_intr || ptc1_intr} }; // LA - assign la_data_out = {{(127-BITS){1'b0}}, count}; - // Assuming LA probes [63:32] are for controlling the count register - assign la_write = ~la_oenb[63:32] & ~{BITS{valid}}; - // Assuming LA probes [65:64] are for controlling the count clk & reset - assign clk = (~la_oenb[64]) ? la_data_in[64]: wb_clk_i; - assign rst = (~la_oenb[65]) ? la_data_in[65]: wb_rst_i; + assign clk = wb_clk_i ; + assign rst = wb_rst_i ; - counter #( - .BITS(BITS) - ) counter( - .clk(clk), - .reset(rst), - .ready(wbs_ack_o), - .valid(valid), - .rdata(rdata), - .wdata(wbs_dat_i), - .wstrb(wstrb), - .la_write(la_write), - .la_input(la_data_in[63:32]), - .count(count) - ); + // Module Address Select Logic + assign pwm1_select = (wbs_adr_i[31:12] == 20'h30001) ; + assign pwm2_select = (wbs_adr_i[31:12] == 20'h30002) ; + assign i2c_select = (wbs_adr_i[31:12] == 20'h30003) ; + assign rtc_select = (wbs_adr_i[31:12] == 20'h30004) ; + assign pid_select = (wbs_adr_i[31:12] == 20'h30005) ; + + // Module STROBE Select based on Address Range + assign pwm1_wbs_stb_i = (wbs_stb_i && pwm1_select) ; + assign pwm2_wbs_stb_i = (wbs_stb_i && pwm2_select) ; + assign i2c_wbs_stb_i = (wbs_stb_i && i2c_select) ; + assign rtc_wbs_stb_i = (wbs_stb_i && rtc_select) ; + assign pid_wbs_stb_i = (wbs_stb_i && pid_select) ; -endmodule -module counter #( - parameter BITS = 32 -)( - input clk, - input reset, - input valid, - input [3:0] wstrb, - input [BITS-1:0] wdata, - input [BITS-1:0] la_write, - input [BITS-1:0] la_input, - output ready, - output [BITS-1:0] rdata, - output [BITS-1:0] count -); - reg ready; - reg [BITS-1:0] count; - reg [BITS-1:0] rdata; + // Slave Acknowledge Response + always @(posedge clk) + wbs_ack_o <= (pwm1_wbs_ack_o || pwm2_wbs_ack_o || i2c_wbs_ack_o || rtc_wbs_ack_o || pid_wbs_ack_o) ; - always @(posedge clk) begin - if (reset) begin - count <= 0; - ready <= 0; - end else begin - ready <= 1'b0; - if (~|la_write) begin - count <= count + 1; - end - if (valid && !ready) begin - ready <= 1'b1; - rdata <= count; - if (wstrb[0]) count[7:0] <= wdata[7:0]; - if (wstrb[1]) count[15:8] <= wdata[15:8]; - if (wstrb[2]) count[23:16] <= wdata[23:16]; - if (wstrb[3]) count[31:24] <= wdata[31:24]; - end else if (|la_write) begin - count <= la_write & la_input; - end - end - end + // Slave Return Data + always @(posedge clk) + if (pwm1_wbs_ack_o) + wbs_dat_o <= pwm1_wbs_dat_o ; + else if (pwm2_wbs_ack_o) + wbs_dat_o <= pwm2_wbs_dat_o ; + else if (i2c_wbs_ack_o) + wbs_dat_o <= {24'b0,i2c_wbs_dat_o} ; + else if (rtc_wbs_ack_o) + wbs_dat_o <= rtc_wbs_dat_o ; + else if (pid_wbs_ack_o) + wbs_dat_o <= pid_wbs_dat_o ; + else + wbs_dat_o <= 32'h0 ; + + + + // PTC1 Module instantiations + ptc_top ptc1_i ( + .wb_clk_i (clk), + .wb_rst_i (rst), + .wb_cyc_i (wbs_cyc_i), + .wb_adr_i ({8'h0,wbs_adr_i[7:0]}), // 16-bit address + .wb_dat_i (wbs_dat_i), + .wb_sel_i (wbs_sel_i), + .wb_we_i (wbs_we_i), + .wb_stb_i (pwm1_wbs_stb_i), + .wb_dat_o (pwm1_wbs_dat_o), + .wb_ack_o (pwm1_wbs_ack_o), + .wb_err_o ( ), + .wb_inta_o (ptc1_intr), + .gate_clk_pad_i (ptc_clk1), + .capt_pad_i (capt_in1), + .pwm_pad_o (pwm_out1), + .oen_padoen_o (pwm_out1_oen) + ); + + // PTC2 Module instantiations + + ptc_top ptc2_i ( + .wb_clk_i (clk), + .wb_rst_i (rst), + .wb_cyc_i (wbs_cyc_i), + .wb_adr_i ({8'h0,wbs_adr_i[7:0]}), // 16-bit address + .wb_dat_i (wbs_dat_i), + .wb_sel_i (wbs_sel_i), + .wb_we_i (wbs_we_i), + .wb_stb_i (pwm2_wbs_stb_i), + .wb_dat_o (pwm2_wbs_dat_o), + .wb_ack_o (pwm2_wbs_ack_o), + .wb_err_o ( ), + .wb_inta_o (ptc2_intr), + .gate_clk_pad_i (ptc_clk2), + .capt_pad_i (capt_in2), + .pwm_pad_o (pwm_out2), + .oen_padoen_o (pwm_out2_oen) + ); + + // I2C Module Instanciation + i2c_master_top i2c_i ( + .wb_clk_i (clk), + .wb_rst_i (rst), + .arst_i (1'b1), + .wb_adr_i (wbs_adr_i[4:2]), // 3-bit address + .wb_dat_i (wbs_dat_i[7:0]), // 8-bit data + .wb_dat_o (i2c_wbs_dat_o), // 8-bit data + .wb_we_i (wbs_we_i), + .wb_stb_i (i2c_wbs_stb_i ), + .wb_cyc_i (wbs_cyc_i), + .wb_ack_o (i2c_wbs_ack_o), + .wb_inta_o (i2c_intr), + .scl_pad_i (i2c_scl_in), + .scl_pad_o (scl_pad_o), + .scl_padoen_o (scl_padoen_o), + .sda_pad_i (i2c_sda_in), + .sda_pad_o (sda_pad_o), + .sda_padoen_o (sda_padoen_o) + ); + + // RTC Module Instanciation + + rtcdate rtc_date_i ( + .i_clk (clk), + .i_ppd (la_data_in[111]), + .i_wb_cyc (wbs_cyc_i), + .i_wb_stb (rtc_wbs_stb_i), + .i_wb_we (wbs_we_i), + .i_wb_data (wbs_dat_i), + .o_wb_ack (rtc_wbs_ack_o), + .o_wb_data (rtc_wbs_dat_o) + ); + +/* + // PID Module Instantiation + PID pid ( + .i_clk (clk), + .i_rst (rst), + .i_wb_cyc (wbs_cyc_i), + .i_wb_stb (pid_wbs_stb_i), + .i_wb_we (wbs_we_i), + .i_wb_adr ({8'h0,wbs_adr_i[7:0]}), // 16-bit address + .i_wb_data (wbs_dat_i), + .o_wb_ack (pid_wbs_ack_o), + .o_wb_data (pid_wbs_dat_o), + .o_un (), + .o_valid () + ); + + + // FPU Instanciation + fpu fpu_i ( + + .clk (clk), + .rmode (la_data_in[105:104]), // 2-bit + .fpu_op (la_data_in[108:106]), // 3-bit + .opa (la_data_in[31:0]), // 32-bit + .opb (la_data_in[63:32]), // 32-bit + .out (la_data_in[95:64]), // 32-bit + .inf (la_data_out[96]), + .snan (la_data_out[97]), + .qnan (la_data_out[98]), + .ine (la_data_out[99]), + .overflow (la_data_out[100]), + .underflow (la_data_out[101]), + .zero (la_data_out[102]), + .div_by_zero (la_data_out[103]), + + ); +*/ endmodule `default_nettype wire