// // K580VI53 timer implementation // // Copyright (c) 2016 Sorgelig // // // This source file is free software: 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 source file 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // altera message_off 10240 `default_nettype none module k580vi53 ( // CPU bus input reset, input clk_sys, input [1:0] addr, input [7:0] din, output [7:0] dout, input wr, input rd, // Timer signals input [2:0] clk_timer, input [2:0] gate, output [2:0] out, output [2:0] sound_active ); wire [7:0] dout0; wire [7:0] dout1; wire [7:0] dout2; assign dout = dout0 & dout1 & dout2; timer t0(reset, clk_sys, clk_timer[0], din, dout0, wr && (addr == 3) && (din[7:6] == 0), wr && (addr == 0), rd && (addr == 0), gate[0], out[0], sound_active[0]); timer t1(reset, clk_sys, clk_timer[1], din, dout1, wr && (addr == 3) && (din[7:6] == 1), wr && (addr == 1), rd && (addr == 1), gate[1], out[1], sound_active[1]); timer t2(reset, clk_sys, clk_timer[2], din, dout2, wr && (addr == 3) && (din[7:6] == 2), wr && (addr == 2), rd && (addr == 2), gate[2], out[2], sound_active[2]); endmodule module timer ( input reset, input clk_sys, input clk_timer, input [7:0] din, output [7:0] dout, input wr_cw, input wr, input rd, input gate, output reg out, output reg sound_active ); reg [7:0] q; reg [7:0] cw; reg [15:0] counter; reg [15:0] ld_count; reg [7:0] load; reg pause; reg stop1; assign dout = q; always @(posedge clk_sys) begin reg [15:0] l_counter; reg msbw, msbr; // according to Siemens doc, read and write have indepenent msb flag. reg latched; reg old_wr_cw, old_wr, old_rd; old_wr_cw <= wr_cw; old_wr <= wr; old_rd <= rd; if(!old_wr_cw && wr_cw) begin msbw <=0; msbr <=0; if(!din[5:4]) begin if(!latched) begin latched <=1; l_counter <= counter; end end else begin cw <= din; latched <=0; stop1 <=1; pause<=1; end end if(!old_wr && wr) begin case(cw[5:4]) 1: begin // high speed mode ld_count[7:0] <= check(din); ld_count[15:8] <= 0; stop1 <=0; load <=load + 1'd1; pause <=0; end 2: begin // low precision mode ld_count[7:0] <= 0; ld_count[15:8] <= check(din); stop1 <=0; load <=load + 1'd1; pause <=0; end default: begin // full mode if(msbw) ld_count[15:8] <= check(din); else ld_count[7:0] <= check(din); msbw <= ~msbw; pause <= ~msbw; if(msbw) begin stop1 <=0; load <=load + 1'd1; end end endcase end if(!old_rd && rd) begin casex({latched, msbr, cw[5:4]}) 4'b0X01: q <=counter[7:0]; 4'b0X10: q <=counter[15:8]; 4'b0011: q <=counter[7:0]; 4'b0111: q <=counter[15:8]; 4'b1X01: begin q <=l_counter[7:0]; latched <=0; end 4'b1X10: begin q <=l_counter[15:8]; latched <=0; end 4'b1011: q <=l_counter[7:0]; 4'b1111: begin q <=l_counter[15:8]; latched <=0; end endcase msbr <= ~msbr; end if(!rd || reset) q <= 255; if(reset) begin stop1 <=1; ld_count <=0; cw <= 0; end end function [15:0] minus1; input [15:0] value; begin if(!cw[0]) minus1 = value-1'd1; else begin minus1 = value; if(!minus1[3:0]) begin minus1[3:0] = 9; if(!minus1[7:4]) begin minus1[7:4] = 9; if(!minus1[11:8]) begin minus1[11:8] = 9; if(!minus1[15:12]) begin minus1[15:12] = 9; end else minus1[15:12] = minus1[15:12]-1'd1; end else minus1[11:8] = minus1[11:8]-1'd1; end else minus1[7:4] = minus1[7:4]-1'd1; end else minus1[3:0] = minus1[3:0]-1'd1; end end endfunction function [7:0] check; input [7:0] value; begin if(!cw[0]) check = value; else begin check[3:0] = (value[3:0]>9) ? 4'd9 : value[3:0]; check[7:4] = (value[7:4]>9) ? 4'd9 : value[7:4]; end; end endfunction reg stop2; wire stop = stop1 | stop2; // // With bugs implemented: // // M0,M1,M4,M5 - counter doesn't stop at the end but wrap around instead. // // M1,M5 - setting of Control Word doesn't reset counter. // Counter continue to count old value after ccounter register is set. // always @(posedge clk_sys) begin reg [7:0] old_load; reg old_gate; reg start, count_en, m3state; reg [7:0] stop_delay; reg old_clk; // cannot treat it as clock enable because timer clock // can be fed from output of other timer old_clk <= clk_timer; if(old_clk & ~clk_timer) begin stop2 <= stop1; old_load <= load; old_gate <= gate; start <= stop; // Assume sound is generated by mode 3 and mode 0(DAC emulation). sound_active <= (!cw[3:1] || (cw[2:1] == 2'b11)) && !stop; casex(cw[3:1]) 3'b000: if(stop) begin out <=0; count_en <=0; end else begin if(start || (old_load != load)) begin counter <= ld_count; out <=0; count_en <=1; end else if(!pause && gate) begin counter <= minus1(counter); if(counter == 1) begin out <=1; count_en <=0; end end end 3'b001: if(stop) begin out <=1; count_en <=0; end else begin if(!old_gate & gate) begin counter <= ld_count; out <=0; count_en <=1; end else begin counter <= minus1(counter); if((counter == 1) && count_en) begin out <=1; count_en <=0; end end end 3'bX10: if(stop || !gate) out <=1; else begin if(start || (!old_gate & gate) || (counter <= 1)) begin counter <=ld_count; out <=1; end else begin counter <= minus1(counter); if(counter == 2) out <=0; end end 3'bX11: if(stop || !gate) begin out <=1; m3state <=1; end else begin if(start || (!old_gate & gate) || (counter <= 2)) begin counter <=ld_count; out <= m3state; m3state <= ~m3state; end else begin counter <= !counter[0] ? minus1(minus1(counter)) : out ? minus1(counter) : minus1(minus1(minus1(counter))); end end 3'b100: if(stop) begin out <=1; count_en <=0; end else begin out <=1; if(start) begin counter <=ld_count; count_en <=1; end else if(gate) begin counter <= minus1(counter); if((counter == 1) && count_en) begin out <=0; count_en <=0; end end end 3'b101: if(stop) begin out <=1; count_en <=0; end else begin out <=1; if(!old_gate & gate) begin counter <=ld_count; out <=1; count_en <=1; end else begin counter <= minus1(counter); if((counter == 1) && count_en) begin out <=0; count_en <=0; end end end endcase end end endmodule