mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-03-01 09:21:14 +00:00
311 lines
7.2 KiB
Verilog
311 lines
7.2 KiB
Verilog
//
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
//
|
|
// 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
|