1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-03-01 09:21:14 +00:00
Files
Gehstock.Mist_FPGA/common/IO/k580vi53.v
Marcel 2ed2acd054 Sync
2024-03-08 11:04:15 +01:00

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