Commit BBU work-in-progress.
For comparative simulation, I'm also assembling the logic of the earlier Macintosh 128K, and possibly also Macintosh Plus, in Verilog, and planning on doing MLB board-level simulations too.
This commit is contained in:
parent
4fa9303844
commit
6678c4b2ab
|
@ -0,0 +1,19 @@
|
|||
TARGETS = bbu.vvp test_stdlogic.vvp test_mac128pal.vvp
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
.SUFFIXES: .v .vvp
|
||||
|
||||
.v.vvp:
|
||||
iverilog -Wanachronisms -Wimplicit -Wportbind -Wselect-range \
|
||||
-Winfloop -Wsensitivity-entire-vector \
|
||||
-Wsensitivity-entire-array \
|
||||
-o $@ $<
|
||||
|
||||
bbu.vvp: bbu.v common.vh
|
||||
test_stdlogic.vvp: test_stdlogic.v stdlogic.v common.vh
|
||||
test_mac128pal.vvp: test_mac128pal.v test_stdlogic.v stdlogic.v \
|
||||
mac128pal.v common.vh
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS)
|
|
@ -1,5 +1,7 @@
|
|||
# "BBU" Apple Custom Silicon
|
||||
|
||||
COMPILING: I use Icarus Verilog (`iverilog`) for simulation.
|
||||
|
||||
The "BBU" (Bob Bailey Unit), as it is called on the Macintosh SE's
|
||||
printed circuit board silkscreen, is a relatively complex Apple custom
|
||||
silicon chip, compared to the other custom chips on the Macintosh SE's
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
/* Synthesizable Verilog hardware description for a drop-in
|
||||
replacement of the Apple Custom Silicon Bob Bailey Unit (BBU), an
|
||||
address controller for the Macintosh SE and similar computers.
|
||||
|
||||
|
||||
Written in 2020 by Andrew Makousky
|
||||
|
||||
|
||||
Public Domain Dedication:
|
||||
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all
|
||||
copyright and related and neighboring rights to this software to
|
||||
the public domain worldwide. This software is distributed without
|
||||
any warranty.
|
||||
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication
|
||||
along with this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
*/
|
||||
|
||||
`ifndef BBU_V
|
||||
`define BBU_V
|
||||
|
||||
`include "common.vh"
|
||||
|
||||
// NOTE: These constants are defined in lower-case because they are
|
||||
// meant to be treated principally as if they are hard-wired
|
||||
// registers.
|
||||
|
@ -172,7 +177,7 @@ module bbu_master_ctrl
|
|||
// MC68000 signals
|
||||
input wire a9, a17, a19, a20, a21, a22, a23;
|
||||
input wire r_n_w, n_as, n_uds, n_lds;
|
||||
inout wire n_dtack;
|
||||
`output_wz wire n_dtack;
|
||||
output wire n_ipl0;
|
||||
input wire n_ipl1;
|
||||
output wire n_berr;
|
||||
|
@ -262,8 +267,8 @@ module bbu_master_ctrl
|
|||
//////////////////////////////////////////////////
|
||||
// Pure combinatorial logic is defined first.
|
||||
|
||||
// TODO: Assert `*IPL0` if we receive an interrupt signal from the
|
||||
// VIA or SCSI. However, do not assert `*IPL0` if the SCC asserts
|
||||
// Assert `*IPL0` if we receive an interrupt signal from the VIA or
|
||||
// SCSI. However, do not assert `*IPL0` if the SCC asserts
|
||||
// `*IPL1`. Guide to the Macintosh family hardware, page 113.
|
||||
// SCSI interrupts are signaled only on IRQ from the SCSI
|
||||
// controller. DRQ is not attached to MC68000 interrupt lines
|
||||
|
@ -663,6 +668,12 @@ endmodule
|
|||
* 0xf00000 - 0xffffef: ??? (the ROM appears to be accessing here)
|
||||
* 0xfffff0 - 0xffffff: Auto Vector
|
||||
|
||||
TODO FIXME: Note that SCSI chip enable is NOT asserted when A9 is
|
||||
one, and Macintosh Plus asserts DACK when A9 is one, but not when
|
||||
it is zero. Okay, so I think I have that figured out. Add 512 for
|
||||
DMA mode access logic, otherwise we do not implement DMA access
|
||||
mode at all.
|
||||
|
||||
This address map has also been confirmed with Guide to the
|
||||
Macintosh family hardware, page 127. PLEASE NOTE: In SCC Read
|
||||
zone, if A0 == 1, then that is an SCC RESET. IWM must be A0 == 1,
|
||||
|
@ -1099,7 +1110,7 @@ module dramctl_cpu (n_res, clk, r_n_w, c2m,
|
|||
// data output/input (RDQ) by placing those lanes in a
|
||||
// high-impedance state.
|
||||
output reg n_en245;
|
||||
// *PMCYC principally enables the row/column address multiplexors.
|
||||
// *PMCYC principally enables the row/column address multiplexers.
|
||||
// At a higher level, it is used to determine whether it is the
|
||||
// CPU's turn to access RAM or the BBU's turn to access RAM. The
|
||||
// CPU always takes a multiple of 4 clock cycles running at 8 MHz
|
||||
|
@ -1112,7 +1123,7 @@ module dramctl_cpu (n_res, clk, r_n_w, c2m,
|
|||
input wire [9:0] ra; // RAM Address (RA)
|
||||
// In order to implement the memory overlay switch, we must snoop
|
||||
// the address bus. These are the registers we use to store the
|
||||
// address multiplexor outputs.
|
||||
// address multiplexer outputs.
|
||||
// N.B. RA7 and RA9 are set by us, but for simplicity of downstream
|
||||
// code, we capture them into the address snooping registers
|
||||
// regardless.
|
||||
|
@ -1196,7 +1207,7 @@ module dramctl_cpu (n_res, clk, r_n_w, c2m,
|
|||
// triggers *PMCYC at the beginning of the CPU's turn to
|
||||
// access memory.
|
||||
// if (drc_state[1]) begin // State 2
|
||||
// // Enable the row address multiplexors.
|
||||
// // Enable the row address multiplexers.
|
||||
// n_pmcyc <= 0;
|
||||
// // Trigger *EN245 as early as possible.
|
||||
// n_en245 <= 0;
|
||||
|
@ -1217,7 +1228,7 @@ module dramctl_cpu (n_res, clk, r_n_w, c2m,
|
|||
drc_state_buf <= drc_state << 1;
|
||||
end
|
||||
if (drc_state[3]) begin // State 8
|
||||
// Enable the column address multiplexors.
|
||||
// Enable the column address multiplexers.
|
||||
c2m <= 1;
|
||||
drc_state_buf <= drc_state << 1;
|
||||
end
|
||||
|
@ -1267,7 +1278,7 @@ module dramctl_bbu (n_res, clk, r_n_w,
|
|||
output wire ram_r_n_w;
|
||||
// Row Address Strobe (*RAS), Column Address Strobe (*CAS)
|
||||
output reg n_ras, n_cas;
|
||||
// *PMCYC principally enables the row/column address multiplexors.
|
||||
// *PMCYC principally enables the row/column address multiplexers.
|
||||
// At a higher level, it is used to determine whether it is the
|
||||
// CPU's turn to access RAM or the BBU's turn to access RAM. The
|
||||
// CPU always takes a multiple of 4 clock cycles running at 8 MHz
|
||||
|
@ -1350,7 +1361,7 @@ module dramctl_bbu (n_res, clk, r_n_w,
|
|||
// end
|
||||
|
||||
if (drc_state[1]) begin // State 2
|
||||
// Enable the row address multiplexors.
|
||||
// Enable the row address multiplexers.
|
||||
ra <= row_addr;
|
||||
drc_state_buf <= drc_state << 1;
|
||||
end
|
||||
|
@ -1360,7 +1371,7 @@ module dramctl_bbu (n_res, clk, r_n_w,
|
|||
drc_state_buf <= drc_state << 1;
|
||||
end
|
||||
if (drc_state[3]) begin // State 8
|
||||
// Enable the column address multiplexors.
|
||||
// Enable the column address multiplexers.
|
||||
ra <= col_addr;
|
||||
drc_state_buf <= drc_state << 1;
|
||||
end
|
||||
|
@ -1506,6 +1517,12 @@ module avtimers ();
|
|||
// less ideal but easier to program would be to use two 16-bit
|
||||
// buffers as a FIFO.
|
||||
|
||||
// N.B. Sound generation. Since the original Macintosh used only
|
||||
// simple counters and the registered ASG PAL for PWM generation,
|
||||
// there is no way the more sophisticated PWM techniques could have
|
||||
// been used. This is going to be a one-shot countdown timer for
|
||||
// generating a single pulse per byte.
|
||||
|
||||
always @(negedge n_res) begin
|
||||
// Initialize all output registers on RESET.
|
||||
|
||||
|
@ -1525,5 +1542,64 @@ endmodule
|
|||
/* TODO: Summary of what is missing and left to implement: DRAM
|
||||
initialization pulses, DRAM refresh, detect 2.5MB of RAM and
|
||||
configure address buffers accordingly, video, disk, and audio
|
||||
scanout, SCSI DMA, EXTDTK yielding, interrupt propagation,
|
||||
double-check SCC read/write logic. */
|
||||
scanout, SCSI DMA, EXTDTK yielding.
|
||||
|
||||
Okay, so the VERDICT on DRAM initialization pulses. We don't
|
||||
actually use these as we should, strictly speaking, but why does it
|
||||
still work? On power-on RESET, the first few CPU memory accesses
|
||||
are all in ROM. Yet the BBU is still scanning the DRAM and
|
||||
fetching words from it. These first few words will be garbage, but
|
||||
it's okay because we're read-only. By the time the CPU makes its
|
||||
first write to DRAM, all is well because it received a sufficient
|
||||
number of *RAS initialization pulses.
|
||||
|
||||
So really, the only mysteries left now is 2.5MB RAM detection and
|
||||
4MB RAM DRAM refresh. Then we need to do the busywork to implement
|
||||
the PWM and video scanout modules and we're done! */
|
||||
|
||||
/*
|
||||
|
||||
Now I think I see why there is the funny thing going on with the
|
||||
address multiplexers for RAS/CAS. It is a required modification to
|
||||
use DRAM fast-page mode since RAS and CAS are still logically
|
||||
"swapped" compared to a contiguous memory layout. This swapping of
|
||||
RAS and CAS is used to get DRAM refresh for free when scanning the
|
||||
video framebuffer.
|
||||
|
||||
Okay, so let's review in more detail.
|
||||
|
||||
Address multiplexer row address outputs:
|
||||
|
||||
A2, A3, A4, A5, A6, A7, A8, A10
|
||||
|
||||
A9 inputs directly to BBU, controls RA7.
|
||||
|
||||
This is a straight match-up to DRAM row address lines.
|
||||
|
||||
RA0 A2
|
||||
RA1 A3
|
||||
RA2 A4
|
||||
RA3 A5
|
||||
RA4 A6
|
||||
RA5 A7
|
||||
RA6 A8
|
||||
RA7 A9
|
||||
RA8 A10
|
||||
RA9 A19 (optional) (!)
|
||||
|
||||
So, how many longwords for the video framebuffer?
|
||||
|
||||
512 x 342 / 32 = 5472 longwords
|
||||
In hex: 0x1560
|
||||
Number of address bits fully covered by a full scan: 12
|
||||
|
||||
Okay, so the question, does it work for DRAM refresh? Indeed it
|
||||
does! Well, at least for <=1MB of RAM.
|
||||
|
||||
RA9 looks to be trouble. But, the Unitron reverse engineering docs
|
||||
almost have a solution. Set this to A17 (?) and it should "just work"
|
||||
I guess. But why?
|
||||
|
||||
*/
|
||||
|
||||
`endif // NOT BBU_V
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
`ifndef COMMON_VH
|
||||
`define COMMON_VH
|
||||
|
||||
// Special type indicator for simclk: it will not be present in
|
||||
// physical builds.
|
||||
`define virtwire wire
|
||||
// Simulate an output wire by using multi-cycle registered logic.
|
||||
`define simwire reg
|
||||
// Tristate output (output With high-impedance (Z))
|
||||
`define output_wz inout
|
||||
// To preserve pin numbering at the I/O connections, power wires can
|
||||
// be implemented as inputs: constant value 1 for Vcc, 0 for GND.
|
||||
`define power input
|
||||
|
||||
`endif // not COMMON_VH
|
|
@ -0,0 +1,400 @@
|
|||
/* Implementation of Macintosh 128k/512k PALs in Verilog,
|
||||
mainly to assist verification of the Macintosh SE BBU. They are
|
||||
not identical, of course, but this should at least be helpful is
|
||||
spotting significant flaws.
|
||||
|
||||
Note that the signal names use here are the same as used in the
|
||||
Unitron Macintosh clone's reverse engineering documentation. Many
|
||||
of the signals are active low but not indicated here, for now.
|
||||
|
||||
Written in 2020 by Andrew Makousky
|
||||
|
||||
Public Domain Dedication:
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all
|
||||
copyright and related and neighboring rights to this software to
|
||||
the public domain worldwide. This software is distributed without
|
||||
any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication
|
||||
along with this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
*/
|
||||
|
||||
/* Note: All arguments are listed in the order of the pinout of each
|
||||
PAL chip.
|
||||
|
||||
Another point, our cheap and easy way to hand self-referential PAL
|
||||
logic equations is to simply treat them as registered sequential
|
||||
logic equations and use enough simulation sub-cycles on them for
|
||||
them to reach settle time. `simclk` controls these sub-cycles.
|
||||
|
||||
Also note, PAL registers do not have a deterministic
|
||||
initialization, hence the absence of a RESET signal.
|
||||
*/
|
||||
|
||||
`ifndef MAC128PAL_V
|
||||
`define MAC128PAL_V
|
||||
|
||||
`include "common.vh"
|
||||
|
||||
// PAL0-16R4: Timing State Machine
|
||||
module tsm(simclk, clk, sysclk, pclk, s1, ramen, romen, as, uds, lds, gnd,
|
||||
oe1, casl, cash, ras, vclk, q2, q1, s0, dtack, vcc);
|
||||
input `virtwire simclk;
|
||||
input wire clk;
|
||||
input wire sysclk, pclk, s1, ramen, romen, as, uds, lds;
|
||||
`power wire gnd;
|
||||
input wire oe1;
|
||||
output `simwire casl, cash;
|
||||
output reg ras, vclk, q2, q1;
|
||||
output `simwire s0, dtack;
|
||||
`power wire vcc;
|
||||
|
||||
// Simulate combinatorial logic sub-cycles.
|
||||
always @(posedge simclk) begin
|
||||
casl <= ~(~s0 & s1 & sysclk // video
|
||||
| ~s0 & ~ramen & ~lds // processor
|
||||
| ~s0 & ~casl & sysclk
|
||||
| pclk & ~casl);
|
||||
cash <= ~(~s0 & s1 & sysclk // video
|
||||
| ~s0 & ~ramen & ~uds // processor
|
||||
| ~s0 & ~cash & sysclk
|
||||
| pclk & ~cash);
|
||||
s0 <= ~(~ras & ~sysclk // 0 for `cas` and 1 for `ras` (estamos contando com o atraso da PAL)
|
||||
| ~ras & ~s0);
|
||||
dtack <= ~(~romen // se a ROM for de 250 nS ou SCC ou IWM
|
||||
| ~ras & ~ramen & ~s1 // garante que vai ser reconhecido na descida de `pclk` no estado `s5`
|
||||
| ~as & ~dtack & ramen // espera `as` subir para desativar
|
||||
| ~as & ~dtack & ~s1); // mas evite ciclas de video (WE)
|
||||
end
|
||||
|
||||
// Simulate registered logic.
|
||||
always @(posedge clk) begin
|
||||
ras <= ~(~pclk & q1 & s1 // video cycle
|
||||
| ~pclk & q1 & ~ramen & dtack // processor cycle
|
||||
| pclk & ~ras); // any other (?) (segura mais um ciclo) cycle
|
||||
vclk <= ~(~q1 & pclk & q2 & vclk // divide by 8 (1MHz)
|
||||
| ~vclk & q1
|
||||
| ~vclk & ~pclk
|
||||
| ~vclk & ~q2);
|
||||
q1 <= ~(~pclk & q1
|
||||
| pclk & ~q1); // divide `pclk` by 2 (4MHz)
|
||||
q2 <= ~(~q1 & pclk & q2 // divide by 4 (2MHz)
|
||||
| ~q2 & q1
|
||||
| ~q2 & ~pclk);
|
||||
end
|
||||
endmodule
|
||||
|
||||
// ras of processor: a16| a8| a7| a6| a5| a4| a3| a2| a1
|
||||
// cas of processor: a17|a16|a15|a14|a13|a12|a11|a10| a9
|
||||
// ras of video: pup| v8| v7| v6| v5| v4| v3| v2| v1
|
||||
// cas of video: pup|pup|vpg|3q1|3q4|v12|v11|v10| v9
|
||||
// ras of sound: pup|3q4|v12|v11|v10|?v8|?v9| v7| v6
|
||||
// cas of sound: pup|pup|pup|spg|pup|spg|spg|spg|3q1
|
||||
|
||||
// PAL1-16R8: Linear Address Generator
|
||||
module lag(simclk, sysclk, p2io1, l28, va4, p0q2, vclk, va3, va2, va1,
|
||||
gnd, oe2, vshft, vsync, hsync, s1, viapb6, snddma,
|
||||
reslin, resnyb, vcc);
|
||||
input `virtwire simclk;
|
||||
input wire sysclk;
|
||||
input wire p2io1, l28, va4, p0q2, vclk, va3, va2, va1;
|
||||
`power wire gnd;
|
||||
input wire oe2;
|
||||
output reg vshft, vsync, hsync, s1, viapb6, snddma, reslin, resnyb;
|
||||
`power wire vcc;
|
||||
|
||||
// Simulate combinatorial logic sub-cycles.
|
||||
always @(posedge simclk) begin
|
||||
end
|
||||
|
||||
// Simulate registered logic.
|
||||
always @(posedge sysclk) begin
|
||||
vshft <= ~(s1 & ~vclk & snddma); // um pulso depois da descida de `vclk`
|
||||
vsync <= ~(reslin
|
||||
| ~vsync & ~l28);
|
||||
hsync <= ~(viapb6 & va4 & ~va3 & ~va2 & va1 // comec,a em29 (VA5)
|
||||
| /*~ ???*/resnyb
|
||||
| ~hsync & viapb6); // termina em 0F
|
||||
s1 <= ~(~p0q2 // 0 for processor and 1 for video
|
||||
| ~vclk
|
||||
| ~vsync & hsync
|
||||
| ~vsync & viapb6 // no vertical retrace s'o temos ciclos de som
|
||||
| ~viapb6 & hsync & ~va4 & ~va3 & ~va2
|
||||
| ~viapb6 & ~hsync & (~va4 | va4 & ~va3 & ~va2 |
|
||||
va4 & ~va3 & va2 & ~va1));
|
||||
viapb6 <= ~(~hsync & resnyb // 1 indicates horizontal retrace (pseudo VA6)
|
||||
| va1 & ~viapb6
|
||||
| va2 & ~viapb6
|
||||
| ~hsync & ~viapb6
|
||||
| resnyb & ~viapb6
|
||||
| vshft & ~viapb6);
|
||||
snddma <= ~(viapb6 & va4 & ~va3 & va2 & va1 & p0q2 & vclk & ~hsync // 0 nesta sa'ida
|
||||
| ~snddma & vclk); // ... indicates sound cycle
|
||||
reslin <= ~(0); // ??? tentamos gerar linha 370
|
||||
resnyb <= ~(vclk // incrementa VA5:VA14 em 0F e 2B
|
||||
| viapb6 // ???
|
||||
| va1
|
||||
| va2
|
||||
| ~viapb6 & va3
|
||||
| hsync
|
||||
| viapb6 & ~va3
|
||||
| ~hsync & va3 & ~va4
|
||||
| ~hsync & ~va3 & va4);
|
||||
end
|
||||
endmodule
|
||||
|
||||
// 32 ciclas atiuas par linha - UA6..UA1 = 0 to 1F
|
||||
// 1 ciclos de som/pwm = 2B
|
||||
// 11 ciclos de retrac,o = 20 to 2A
|
||||
|
||||
// 342 linhas ativas - VA6..VA14 = 010011100 to 111110001
|
||||
// 28 linhas de retrac,o = 010000000 to 010011011
|
||||
|
||||
// PAL2-16L8: Bus Management Unit 1
|
||||
module bmu1(simclk, va9, va8, va7, l15, va14, ovlay, a23, a22, a21, gnd,
|
||||
as, csiwm, rd, cescc, vpa, romen, ramen, io1, l28, vcc);
|
||||
input `virtwire simclk;
|
||||
input wire va9, va8, va7, l15, va14, ovlay, a23, a22, a21;
|
||||
`power wire gnd;
|
||||
input wire as;
|
||||
output `simwire csiwm, rd, cescc, vpa, romen, ramen, io1, l28;
|
||||
`power wire vcc;
|
||||
|
||||
// Simulate combinatorial logic sub-cycles.
|
||||
always @(posedge simclk) begin
|
||||
csiwm <= ~(a23 & a22 & ~a21 & ~as); // DFE1FF
|
||||
rd <= ~(a23 & ~a22 & ~a21 & ~as); // 9FFFF8
|
||||
cescc <= ~(a23 & ~a22 & ~as); // 9FFFF8(R) or BFFFF9(W)
|
||||
vpa <= ~(a23 & a22 & a21 & ~as); // acima de E00000 'e s'incrano
|
||||
romen <= ~(~a23 & a22 & ~a21 & ~as // 400000
|
||||
| ~a23 & ~a22 & ~a21 & ~as & ovlay // (and 000000 with `ovlay`)
|
||||
| a23 & ~a22 & ~as
|
||||
| a23 & ~a21 & ~as); // para gerar DTACK (n~ao acessa ROM: A20)
|
||||
ramen <= ~(~a23 & ~a22 & ~a21 & ~as & ~ovlay // 000000
|
||||
| ~a23 & a22 & a21 & ~as & ovlay); // (600000 with `ovlay`)
|
||||
io1 <= ~(0); // ???
|
||||
l28 <= ~(~l15 & ~va9 & ~va8 & va7 // chegamos a 370 ou n~ao passamos da limha 28
|
||||
| ~l28 & ~va9
|
||||
| ~l28 & ~va8
|
||||
| ~l28 & ~va7);
|
||||
end
|
||||
endmodule
|
||||
|
||||
// PAL3-16R4: Bus Management Unit 0
|
||||
module bmu0(simclk, sysclk, ramen, romen, va10, va11, va12, va13, va14, rw,
|
||||
gnd, oe1, g244, we, ava14, l15, vid, ava13, servid, dtack, vcc);
|
||||
input `virtwire simclk;
|
||||
input wire sysclk;
|
||||
input wire ramen, romen, va10, va11, va12, va13, va14, rw;
|
||||
`power wire gnd;
|
||||
input wire oe1;
|
||||
output `simwire g244, we;
|
||||
output reg ava14, l15, vid, ava13;
|
||||
// N.B. Although this is nominally an output we can treat it as an
|
||||
// input?
|
||||
input wire servid, dtack;
|
||||
`power wire vcc;
|
||||
|
||||
// Simulate combinatorial logic sub-cycles.
|
||||
always @(posedge simclk) begin
|
||||
g244 <= ~(~ramen & rw
|
||||
| ~g244 & ~ramen);
|
||||
we <= ~(~ramen & ~rw
|
||||
| ~we & ~dtack); // o dtack 'e mais curto antes de ciclo de video
|
||||
end
|
||||
|
||||
// Simulate registered logic.
|
||||
always @(posedge sysclk) begin
|
||||
ava14 <= ~(~va14 & ~va13); // + 1
|
||||
l15 <= ~(~va14 & ~va13 & ~va12 & ~va11 & ~va10 // n~ao passamos da linha 15
|
||||
| va14 & ~va13 & va12 & va11 & va10); // passamos de 368
|
||||
vid <= ~(servid); // aqui estamos invertendo: blanking est'a em `vshft`
|
||||
ava13 <= ~(va13); // + 1
|
||||
end
|
||||
endmodule
|
||||
|
||||
// PAL4-16R6: Timing Signal Generator
|
||||
module tsg(simclk, sysclk, vpa, a19, vclk, p0q1, e, keyclk, intscc, intvia,
|
||||
gnd, oe3, d0, q6, clkscc, q4, q3, viacb1, pclk, ipl0, vcc);
|
||||
input `virtwire simclk;
|
||||
input wire sysclk;
|
||||
input wire vpa, a19, vclk, p0q1, e, keyclk, intscc, intvia;
|
||||
`power wire gnd;
|
||||
input wire oe3;
|
||||
output `simwire d0;
|
||||
output reg q6, clkscc, q4, q3, viacb1, pclk;
|
||||
output `simwire ipl0;
|
||||
`power wire vcc;
|
||||
|
||||
// Simulate combinatorial logic sub-cycles.
|
||||
always @(posedge simclk) begin
|
||||
ipl0 <= ~intscc | intvia; // CORRECTION
|
||||
// ipl0 <= ~(0); // ??? /M nanda
|
||||
d0 <= ~(~vpa & ~a19 & e); // F00000 amostra a fase como 0 /n e' + usado
|
||||
end
|
||||
|
||||
// Simulate registered logic.
|
||||
always @(posedge sysclk) begin
|
||||
// TODO VERIFY: q6 missing?
|
||||
q6 <= ~(0);
|
||||
clkscc <= ~(clkscc & ~pclk & ~q4
|
||||
| clkscc & ~pclk & ~q3
|
||||
| clkscc & ~pclk & vclk
|
||||
| ~clkscc & pclk
|
||||
| ~clkscc & q4 & q3 & ~vclk); // a cada 32 ciclos n~ao vira
|
||||
viacb1 <= ~(0); // ??? /M nanda
|
||||
pclk <= ~(pclk); // divide SYSCLK por 2 (8MHz)
|
||||
q3 <= ~(~vclk); // `sysclk` / 16
|
||||
q4 <= ~(q4 & q3 & ~vclk // `sysclk` / 32
|
||||
| ~q4 & ~q3 // } J p/gerar CLKSCC
|
||||
| ~q4 & vclk);
|
||||
end
|
||||
endmodule
|
||||
|
||||
/* TODO: Now in order to fully implement the Macintosh's custom board
|
||||
capabilities, we must as a baseline have an implementation of some
|
||||
standard logic chips that are found on the Macintosh Main Logic
|
||||
Board. This is where we implement the modules. */
|
||||
`include "stdlogic.v"
|
||||
|
||||
// Wire that PAL cluster together, along with supporting standard
|
||||
// logic chip. Here, we try to better indicate active high and active
|
||||
// low because we also need to stick in a hex inverter chip.
|
||||
module palcl();
|
||||
input `virtwire simclk;
|
||||
`power wire vcc;
|
||||
`power wire gnd;
|
||||
input wire n_res;
|
||||
input wire n_sysclk; // 16MHz
|
||||
|
||||
// Clocks
|
||||
// 8,4,3.686,2,1,1,0.5 MHz
|
||||
output wire pclk, p0q1, clkscc, p0q2, vclk, q3, q4;
|
||||
input wire e; // 6800 synchronous I/O "E" clock, ~1MHz
|
||||
input wire keyclk;
|
||||
|
||||
// TODO: Also implement dual video address counter ICs as part of
|
||||
// the PAL cluster.
|
||||
// Video address signals, comes from video counter IC
|
||||
input wire va14, va13, va12, va11, va10, va9, va8, va7,
|
||||
va4, va3, va2, va1;
|
||||
// Audio address signals?
|
||||
output wire ava14, ava13;
|
||||
|
||||
// Video control signals
|
||||
output wire n_vshft, n_vsync, n_hsync, n_snddma, vid;
|
||||
input wire servid;
|
||||
|
||||
// MC68000 CPU address signals
|
||||
input wire a23, a22, a21, a20, a19, a17, a9;
|
||||
input wire n_as, n_uds, n_lds;
|
||||
output wire n_dtack;
|
||||
input wire r_n_w;
|
||||
inout wire d0, d1, d2, d3, d4, d5, d6, d7,
|
||||
d8, d9, d10, d11, d12, d13, d14, d15;
|
||||
|
||||
// Chip enable signals
|
||||
output wire n_ramen, n_romen, n_csiwm, n_sccrd, n_cescc, n_vpa;
|
||||
|
||||
// Interrupt signals
|
||||
input wire n_intscc, n_intvia;
|
||||
output wire n_ipl0;
|
||||
|
||||
// VIA signals
|
||||
output wire viapb6; // horizontal blanking
|
||||
input wire ovlay; // Boot-time overlay
|
||||
output wire viacb1; // keyboard interrupt
|
||||
// wire d0; // Video timing phase sense signal
|
||||
|
||||
// DRAM signals
|
||||
output wire casl, cash, ras, we;
|
||||
inout wire rdq0, rdq1, rdq2, rdq3, rdq4, rdq5, rdq6, rdq7,
|
||||
rdq8, rdq9, rdq10, rdq11, rdq12, rdq13, rdq14, rdq15;
|
||||
|
||||
// Address multiplexer signals?
|
||||
output wire s0, s1, l28, l15, g244;
|
||||
|
||||
// PAL chip-select and unknown "IO" signals
|
||||
wire tsm_oe1, lag_oe2, bmu0_oe1, tsg_oe3;
|
||||
|
||||
// Internal PAL cluster use only, supports video
|
||||
wire reslin, resnyb; // video counter controllers?
|
||||
wire p2io1, q6;
|
||||
|
||||
// Wires to/from standard logic chips.
|
||||
wire sysclk, snddma, n_a20, n_sndres, sndres, n_snd, snd, wr, n_wr;
|
||||
wire n_245oe;
|
||||
|
||||
// N.B.: *WR comes from IWM chip, WR goes to floppy drives. *A20
|
||||
// goes to VIA.CS1. *SNDDMA comes from the LAG. *SYSCLK comes
|
||||
// from the 16MHz crystal oscillator.
|
||||
|
||||
// *DMALD is generated by ASG.
|
||||
|
||||
wire c8mf, c16mf, n_dmald, u12f_tc, ram_r_n_w_f;
|
||||
wire vmsh; // video mid-shift, connect two register chips together
|
||||
wire n_lermd; // ???
|
||||
wire n_ldps;
|
||||
wire s5;
|
||||
wire ra8, ra9;
|
||||
|
||||
wire vid_n_a4; // ???
|
||||
|
||||
// TODO FIXME! We're not using assign correctly! `assign` implies
|
||||
// diode isolation between separate nets. We want to merge
|
||||
// multiple names together for the same net.
|
||||
|
||||
// N.B. on PCB, use c8mf for high-frequency signal
|
||||
// filter/conditioning.
|
||||
assign c8mf = pclk;
|
||||
assign c16mf = sysclk;
|
||||
assign ram_r_n_w_f = we;
|
||||
|
||||
/* N.B. The reason why phase calibration is required in the
|
||||
Macintosh is because the PALs do not have a RESET pin. It is
|
||||
the logic designer's discretion to implement one explicitly, of
|
||||
they could forgo it to allow for more I/O pins. Hence the
|
||||
motivation to use software phase correction instead. */
|
||||
|
||||
f04 u4d(n_sysclk, sysclk, n_snddma, snddma, a20, n_a20, gnd,
|
||||
n_sndres, sndres, n_snd, snd, wr, n_wr, vcc);
|
||||
|
||||
ls161 u13e(n_sndres, c8mf, rdq12, rdq13, rdq14, rdq15, n_snd, gnd,
|
||||
n_dmald, u12f_tc, , , , , snd, vcc);
|
||||
ls161 u12f(n_sndres, c8mf, rdq8, rdq9, rdq10, rdq11, n_snd, gnd,
|
||||
n_dmald, n_sndres, , , , , u12f_tc, vcc);
|
||||
ls166 u10f(vmsh, rdq8, rdq9, rdq10, rdq11, 1'b0, c16mf, gnd,
|
||||
s5, rdq12, rdq13, rdq14, n_lermd, rdq15, n_ldps, vcc);
|
||||
ls166 u11f(s5, rdq0, rdq1, rdq2, rdq3, 1'b0, c16mf, gnd,
|
||||
s5, rdq4, rdq5, rdq6, vmsh, rdq7, n_ldps, vcc);
|
||||
ls245 u9e(ram_r_n_w_f, rdq0, rdq1, rdq2, rdq3, rdq4, rdq5, rdq6, rdq7,
|
||||
gnd, d7, d6, d5, d4, d3, d2, d1, d0, n_245oe, vcc);
|
||||
ls245 u10e(ram_r_n_w_f, rdq8, rdq9, rdq10, rdq11, rdq12, rdq13, rdq14,
|
||||
rdq15, gnd, d15, d14, d13, d12, d11, d10, d9, d8,
|
||||
n_245oe, vcc);
|
||||
f253 u10g(snddma, vid_n_a4, va7, s5, a9, a17, ra8, gnd,
|
||||
ra9, a20, a19, s5, s5, p0q2/*c2m*/, 1'b0, vcc);
|
||||
|
||||
// asg u11e(c16mf, rdq0, rdq1, rdq2, rdq3, rdq4, rdq5, n_dma, vclk, gnd,
|
||||
// tsen2, n_dmald, pwm, , , , , , , vcc);
|
||||
|
||||
tsm pal0(simclk, sysclk, sysclk, pclk, s1, n_ramen, n_romen, n_as, n_uds, n_lds,
|
||||
gnd, tsm_oe1, casl, cash, ras, vclk, p0q2, p0q1, s0, n_dtack, vcc);
|
||||
lag pal1(simclk, sysclk, p2io1, l28, va4, p0q2, vclk, va3, va2, va1,
|
||||
gnd, lag_oe2, n_vshft, n_vsync, n_hsync, s1, viapb6,
|
||||
n_snddma, reslin,
|
||||
resnyb, vcc);
|
||||
bmu1 pal2(simclk, va9, va8, va7, l15, va14, ovlay, a23, a22, a21, gnd,
|
||||
n_as, n_csiwm, n_sccrd, n_cescc, n_vpa, n_romen, n_ramen, p2io1, l28, vcc);
|
||||
bmu0 pal3(simclk, sysclk, n_ramen, n_romen, va10, va11, va12, va13, va14,
|
||||
r_n_w, gnd, bmu0_oe1, g244, we, ava14, l15, vid, ava13,
|
||||
servid, n_dtack, vcc);
|
||||
tsg pal4(simclk, sysclk, n_vpa, a19, vclk, p0q1, e, keyclk, n_intscc,
|
||||
n_intvia, gnd, tsg_oe3, d0, q6, clkscc, q4, q3, viacb1,
|
||||
pclk, n_ipl0, vcc);
|
||||
endmodule
|
||||
|
||||
`endif // not MAC128PAL_V
|
|
@ -0,0 +1,426 @@
|
|||
/* Implementation of standard logic integrated circuits in Verilog to
|
||||
facilitate board-level simulations of glue logic. DIP pinout.
|
||||
|
||||
Written in 2020 by Andrew Makousky
|
||||
|
||||
Public Domain Dedication:
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all
|
||||
copyright and related and neighboring rights to this software to
|
||||
the public domain worldwide. This software is distributed without
|
||||
any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication
|
||||
along with this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
*/
|
||||
|
||||
`ifndef STDLOGIC_V
|
||||
`define STDLOGIC_V
|
||||
|
||||
`include "common.vh"
|
||||
|
||||
// F00: Quad NAND gates.
|
||||
module f00(s1a, s1b, s1y, s2a, s2b, s2y, gnd,
|
||||
s3y, s3b, s3a, s4y, s4b, s4a, vcc);
|
||||
input wire s1a, s1b;
|
||||
output wire s1y;
|
||||
input wire s2a, s2b;
|
||||
output wire s2y;
|
||||
`power wire gnd;
|
||||
output wire s3y;
|
||||
input wire s3b, s3a;
|
||||
output wire s4y;
|
||||
input wire s4b, s4a;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = ~(s1a & s1b);
|
||||
assign s2y = ~(s2a & s2b);
|
||||
assign s3y = ~(s3a & s3b);
|
||||
assign s4y = ~(s4a & s4b);
|
||||
endmodule
|
||||
|
||||
// F02: Quad NOR gates.
|
||||
module f02(s1y, s1a, s1b, s2y, s2a, s2b, gnd,
|
||||
s3a, s3b, s3y, s4a, s4b, s4y, vcc);
|
||||
output wire s1y;
|
||||
input wire s1a, s1b;
|
||||
output wire s2y;
|
||||
input wire s2a, s2b;
|
||||
`power wire gnd;
|
||||
input wire s3a, s3b;
|
||||
output wire s3y;
|
||||
input wire s4a, s4b;
|
||||
output wire s4y;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = ~(s1a | s1b);
|
||||
assign s2y = ~(s2a | s2b);
|
||||
assign s3y = ~(s3a | s3b);
|
||||
assign s4y = ~(s4a | s4b);
|
||||
endmodule
|
||||
|
||||
// F04: Hex inverters.
|
||||
module f04(a0, q0, a1, q1, a2, q2, gnd, q5, a5, q4, a4, q3, a3, vcc);
|
||||
input wire a0;
|
||||
output wire q0;
|
||||
input wire a1;
|
||||
output wire q1;
|
||||
input wire a2;
|
||||
output wire q2;
|
||||
`power wire gnd;
|
||||
output wire q5;
|
||||
input wire a5;
|
||||
output wire q4;
|
||||
input wire a4;
|
||||
output wire q3;
|
||||
input wire a3;
|
||||
`power wire vcc;
|
||||
|
||||
assign q0 = ~a0;
|
||||
assign q1 = ~a1;
|
||||
assign q2 = ~a2;
|
||||
assign q3 = ~a3;
|
||||
assign q4 = ~a4;
|
||||
assign q5 = ~a5;
|
||||
endmodule
|
||||
|
||||
// F08: Quad AND gates.
|
||||
module f08(s1a, s1b, s1y, s2a, s2b, s2y, gnd,
|
||||
s3y, s3a, s3b, s4y, s4a, s4b, vcc);
|
||||
input wire s1a, s1b;
|
||||
output wire s1y;
|
||||
input wire s2a, s2b;
|
||||
output wire s2y;
|
||||
`power wire gnd;
|
||||
output wire s3y;
|
||||
input wire s3a, s3b;
|
||||
output wire s4y;
|
||||
input wire s4a, s4b;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = s1a & s1b;
|
||||
assign s2y = s2a & s2b;
|
||||
assign s3y = s3a & s3b;
|
||||
assign s4y = s4a & s4b;
|
||||
endmodule
|
||||
|
||||
// F32: Quad OR gates.
|
||||
module f32(s1a, s1b, s1y, s2a, s2b, s2y, gnd,
|
||||
s3y, s3a, s3b, s4y, s4a, s4b, vcc);
|
||||
input wire s1a, s1b;
|
||||
output wire s1y;
|
||||
input wire s2a, s2b;
|
||||
output wire s2y;
|
||||
`power wire gnd;
|
||||
output wire s3y;
|
||||
input wire s3a, s3b;
|
||||
output wire s4y;
|
||||
input wire s4a, s4b;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = s1a | s1b;
|
||||
assign s2y = s2a | s2b;
|
||||
assign s3y = s3a | s3b;
|
||||
assign s4y = s4a | s4b;
|
||||
endmodule
|
||||
|
||||
// LS161: 4-bit binary counter.
|
||||
// Used to generate sequential video and sound RAM addresses.
|
||||
module ls161(n_clr, clk, a, b, c, d, enp, gnd,
|
||||
n_load, ent, q_d, q_c, q_b, q_a, rco, vcc);
|
||||
input wire n_clr, clk, a, b, c, d, enp;
|
||||
`power wire gnd;
|
||||
input wire n_load, ent;
|
||||
output wire q_d, q_c, q_b, q_a;
|
||||
output wire rco;
|
||||
`power wire vcc;
|
||||
|
||||
wire [3:0] loadvec = { d, c, b, a };
|
||||
reg [3:0] outvec;
|
||||
|
||||
assign rco = (outvec == 4'hf);
|
||||
assign { q_d, q_c, q_b, q_a } = outvec;
|
||||
|
||||
// N.B. As soon as the rising edge of the clock is detected under
|
||||
// the proper conditions, we propagate the incremented value to the
|
||||
// output pins. We do not wait until the next rising edge of the
|
||||
// clock.
|
||||
always @(posedge clk) begin
|
||||
if (~n_clr) outvec <= 0;
|
||||
else if (~n_load) outvec <= loadvec;
|
||||
else if (enp & ent)
|
||||
outvec <= outvec + 1;
|
||||
// else Nothing to be done.
|
||||
end
|
||||
endmodule
|
||||
|
||||
// LS165: 8-bit Parallel In, Serial Out shift register. Very similar
|
||||
// to LS166 but *Q_H instead of *CLR and different pinout.
|
||||
module ls165(sh_n_ld, clk, e, f, g, h, n_q_h, gnd,
|
||||
q_h, ser, a, b, c, d, clk_inh, vcc);
|
||||
input wire sh_n_ld, clk, e, f, g, h;
|
||||
output wire n_q_h;
|
||||
`power wire gnd;
|
||||
output wire q_h;
|
||||
input wire ser, a, b, c, d, clk_inh;
|
||||
`power wire vcc;
|
||||
|
||||
wire n_int_clk = ~(clk_inh | clk);
|
||||
reg [7:0] int_reg;
|
||||
|
||||
assign q_h = int_reg[7];
|
||||
assign n_q_h = ~q_h;
|
||||
|
||||
// N.B. As soon as the falling edge of the clock is detected under
|
||||
// the proper conditions, we propagate the shifted value to the
|
||||
// output pins. We do not wait until the next falling edge of the
|
||||
// clock.
|
||||
always @(negedge n_int_clk) begin
|
||||
if (sh_n_ld)
|
||||
int_reg <= { int_reg[6:0], ser };
|
||||
else
|
||||
int_reg <= { h, g, f, e, d, c, b, a };
|
||||
end
|
||||
endmodule
|
||||
|
||||
// LS166: 8-bit Parallel In, Serial Out shift register.
|
||||
// Used to generate the TTL serial video signal.
|
||||
module ls166(ser, a, b, c, d, clk_inh, clk, gnd,
|
||||
n_clr, e, f, g, q_h, h, sh_n_ld, vcc);
|
||||
input wire ser, a, b, c, d, clk_inh, clk;
|
||||
`power wire gnd;
|
||||
input wire n_clr, e, f, g;
|
||||
output wire q_h;
|
||||
input wire h, sh_n_ld;
|
||||
`power wire vcc;
|
||||
|
||||
wire n_int_clk = ~(clk_inh | clk);
|
||||
reg [7:0] int_reg;
|
||||
|
||||
assign q_h = int_reg[7];
|
||||
|
||||
always @(negedge n_clr)
|
||||
int_reg <= 0;
|
||||
|
||||
// N.B. As soon as the falling edge of the clock is detected under
|
||||
// the proper conditions, we propagate the shifted value to the
|
||||
// output pins. We do not wait until the next falling edge of the
|
||||
// clock.
|
||||
always @(negedge n_int_clk) begin
|
||||
if (n_clr) begin
|
||||
if (sh_n_ld)
|
||||
int_reg <= { int_reg[6:0], ser };
|
||||
else
|
||||
int_reg <= { h, g, f, e, d, c, b, a };
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
// LS245: Octal bus transceivers.
|
||||
// Used to control DRAM access from the CPU data bus.
|
||||
module ls245(dir, a1, a2, a3, a4, a5, a6, a7, a8, gnd,
|
||||
b8, b7, b6, b5, b4, b3, b2, b1, n_oe, vcc);
|
||||
input wire dir;
|
||||
inout wire a1, a2, a3, a4, a5, a6, a7, a8;
|
||||
`power wire gnd;
|
||||
inout wire b8, b7, b6, b5, b4, b3, b2, b1;
|
||||
input wire n_oe;
|
||||
`power wire vcc;
|
||||
|
||||
// (~dir) => (ax <= bx), (dir) => (bx <= ax)
|
||||
|
||||
// N.B. `assign` implements diode isolation to enforce a
|
||||
// directional output drive, so we can't assemble bi-directional
|
||||
// bit vectors for more compact code in that fashion. `alias` from
|
||||
// System Verilog would make that possible, though. Though we
|
||||
// could define a sub-module to use bit vectors, it turns out that
|
||||
// consumes just as many lines of code. So we just go repetitive.
|
||||
|
||||
assign a1 = (n_oe | dir) ? 8'bz : b1;
|
||||
assign a2 = (n_oe | dir) ? 8'bz : b2;
|
||||
assign a3 = (n_oe | dir) ? 8'bz : b3;
|
||||
assign a4 = (n_oe | dir) ? 8'bz : b4;
|
||||
assign a5 = (n_oe | dir) ? 8'bz : b5;
|
||||
assign a6 = (n_oe | dir) ? 8'bz : b6;
|
||||
assign a7 = (n_oe | dir) ? 8'bz : b7;
|
||||
assign a8 = (n_oe | dir) ? 8'bz : b8;
|
||||
|
||||
assign b1 = (n_oe | ~dir) ? 8'bz : a1;
|
||||
assign b2 = (n_oe | ~dir) ? 8'bz : a2;
|
||||
assign b3 = (n_oe | ~dir) ? 8'bz : a3;
|
||||
assign b4 = (n_oe | ~dir) ? 8'bz : a4;
|
||||
assign b5 = (n_oe | ~dir) ? 8'bz : a5;
|
||||
assign b6 = (n_oe | ~dir) ? 8'bz : a6;
|
||||
assign b7 = (n_oe | ~dir) ? 8'bz : a7;
|
||||
assign b8 = (n_oe | ~dir) ? 8'bz : a8;
|
||||
endmodule
|
||||
|
||||
// F138: 3-to-8 line decoder/demultiplexer, active low.
|
||||
module f138(a0, a1, a2, n_e1, n_e2, e3, n_y7, gnd,
|
||||
n_y6, n_y5, n_y4, n_y3, n_y2, n_y1, n_y0, vcc);
|
||||
input wire a0, a1, a2, n_e1, n_e2, e3;
|
||||
output wire n_y7;
|
||||
`power wire gnd;
|
||||
output wire n_y6, n_y5, n_y4, n_y3, n_y2, n_y1, n_y0;
|
||||
`power wire vcc;
|
||||
|
||||
wire [2:0] va;
|
||||
wire en;
|
||||
|
||||
assign va = { a2, a1, a0 };
|
||||
assign en = ~n_e1 & ~n_e2 & e3;
|
||||
|
||||
assign n_y0 = ~(en & (va == 0));
|
||||
assign n_y1 = ~(en & (va == 1));
|
||||
assign n_y2 = ~(en & (va == 2));
|
||||
assign n_y3 = ~(en & (va == 3));
|
||||
assign n_y4 = ~(en & (va == 4));
|
||||
assign n_y5 = ~(en & (va == 5));
|
||||
assign n_y6 = ~(en & (va == 6));
|
||||
assign n_y7 = ~(en & (va == 7));
|
||||
endmodule
|
||||
|
||||
// F238: 3-to-8 line decoder/demultiplexer, active high.
|
||||
module f238(a0, a1, a2, n_e1, n_e2, e3, y7, gnd,
|
||||
y6, y5, y4, y3, y2, y1, y0, vcc);
|
||||
input wire a0, a1, a2, n_e1, n_e2, e3;
|
||||
output wire y7;
|
||||
`power wire gnd;
|
||||
output wire y6, y5, y4, y3, y2, y1, y0;
|
||||
`power wire vcc;
|
||||
|
||||
wire [2:0] va;
|
||||
wire en;
|
||||
|
||||
assign va = { a2, a1, a0 };
|
||||
assign en = ~n_e1 & ~n_e2 & e3;
|
||||
|
||||
assign y0 = en & (va == 0);
|
||||
assign y1 = en & (va == 1);
|
||||
assign y2 = en & (va == 2);
|
||||
assign y3 = en & (va == 3);
|
||||
assign y4 = en & (va == 4);
|
||||
assign y5 = en & (va == 5);
|
||||
assign y6 = en & (va == 6);
|
||||
assign y7 = en & (va == 7);
|
||||
endmodule
|
||||
|
||||
// F253: Dual 4-to-1 multiplexer.
|
||||
// Used for Macintosh Plus CPU/video/sound address selection.
|
||||
module f253(n_1g, b, s1c3, s1c2, s1c1, s1c0, s1y, gnd,
|
||||
s2y, s2c0, s2c1, s2c2, s2c3, a, n_2g, vcc);
|
||||
input wire n_1g, b, s1c3, s1c2, s1c1, s1c0;
|
||||
`output_wz wire s1y;
|
||||
`power wire gnd;
|
||||
`output_wz wire s2y;
|
||||
input wire s2c0, s2c1, s2c2, s2c3, a, n_2g;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = (n_1g) ? 'bz : sel1({ b, a });
|
||||
function sel1(input [1:0] selvec);
|
||||
case (selvec)
|
||||
0: sel1 = s1c0;
|
||||
1: sel1 = s1c1;
|
||||
2: sel1 = s1c2;
|
||||
3: sel1 = s1c3;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
assign s2y = (n_2g) ? 'bz : sel2({ b, a });
|
||||
function sel2(input [1:0] selvec);
|
||||
case (selvec)
|
||||
0: sel2 = s2c0;
|
||||
1: sel2 = s2c1;
|
||||
2: sel2 = s2c2;
|
||||
3: sel2 = s2c3;
|
||||
endcase
|
||||
endfunction
|
||||
endmodule
|
||||
|
||||
// F257: Quadruple 2-to-1 multiplexer. Used for RAS/CAS selection.
|
||||
// LS257: Logically the same as F257. (Only differs electrically.)
|
||||
module f257(n_a_b, s1a, s1b, s1y, s2a, s2b, s2y, gnd,
|
||||
s3y, s3b, s3a, s4y, s4b, s4a, n_oe, vcc);
|
||||
input wire n_a_b, s1a, s1b;
|
||||
`output_wz wire s1y;
|
||||
input wire s2a, s2b;
|
||||
`output_wz wire s2y;
|
||||
`power wire gnd;
|
||||
`output_wz wire s3y;
|
||||
input wire s3b, s3a;
|
||||
`output_wz wire s4y;
|
||||
input wire s4b, s4a, n_oe;
|
||||
`power wire vcc;
|
||||
|
||||
assign s1y = (n_oe) ? 'bz : (~n_a_b) ? s1a : s1b;
|
||||
assign s2y = (n_oe) ? 'bz : (~n_a_b) ? s2a : s2b;
|
||||
assign s3y = (n_oe) ? 'bz : (~n_a_b) ? s3a : s3b;
|
||||
assign s4y = (n_oe) ? 'bz : (~n_a_b) ? s4a : s4b;
|
||||
endmodule
|
||||
|
||||
// LS393: Dual 4-bit binary counter.
|
||||
// Used to generate sequential video and sound RAM addresses.
|
||||
module ls393(s1a, s1clr, s1q_a, s1q_b, s1q_c, s1q_d, gnd,
|
||||
s2q_d, s2q_c, s2q_b, s2q_a, s2clr, s2a, vcc);
|
||||
input wire s1a, s1clr;
|
||||
output wire s1q_a, s1q_b, s1q_c, s1q_d;
|
||||
`power wire gnd;
|
||||
output wire s2q_d, s2q_c, s2q_b, s2q_a;
|
||||
input wire s2clr, s2a;
|
||||
`power wire vcc;
|
||||
|
||||
reg [3:0] s1reg;
|
||||
reg [3:0] s2reg;
|
||||
|
||||
assign { s1q_d, s1q_c, s1q_b, s1q_a } = s1reg;
|
||||
assign { s2q_d, s2q_c, s2q_b, s2q_a } = s2reg;
|
||||
|
||||
always @(posedge s1clr)
|
||||
s1reg <= 0;
|
||||
always @(posedge s1a)
|
||||
s1reg <= s1reg + 1;
|
||||
always @(posedge s2clr)
|
||||
s2reg <= 0;
|
||||
always @(posedge s2a)
|
||||
s2reg <= s2reg + 1;
|
||||
|
||||
endmodule
|
||||
|
||||
// LS595: 8-bit serial input, parallel output shift register, with
|
||||
// output latch.
|
||||
module ls595(q_b, q_c, q_d, q_e, q_f, q_g, q_h, gnd,
|
||||
n_q_h, n_srclr, srclk, rclk, n_oe, ser, vcc);
|
||||
output wire q_b, q_c, q_d, q_e, q_f, q_g, q_h;
|
||||
`power wire gnd;
|
||||
output wire n_q_h;
|
||||
input wire n_srclr, srclk, rclk, n_oe, ser;
|
||||
output wire q_a;
|
||||
`power wire vcc;
|
||||
|
||||
reg [7:0] int_reg;
|
||||
reg [7:0] out_reg;
|
||||
|
||||
assign { q_h, q_g, q_f, q_e, q_d, q_c, q_b, q_a }
|
||||
= (n_oe) ? 8'bz : int_reg;
|
||||
assign n_q_h = q_h;
|
||||
|
||||
always @(negedge n_srclr) begin
|
||||
int_reg <= 0;
|
||||
end
|
||||
|
||||
always @(posedge srclk) begin
|
||||
int_reg = { int_reg[7:1], ser };
|
||||
end
|
||||
|
||||
// N.B. As soon as the rising edge of the clock is detected under
|
||||
// the proper conditions, we propagate the shifted value to the
|
||||
// output pins. We do not wait until the next rising edge of the
|
||||
// clock.
|
||||
always @(posedge rclk) begin
|
||||
out_reg <= int_reg;
|
||||
end
|
||||
endmodule
|
||||
|
||||
`endif // not STDLOGIC_V
|
|
@ -0,0 +1,31 @@
|
|||
`timescale 1ns/100ps
|
||||
|
||||
`include "mac128pal.v"
|
||||
|
||||
`include "test_stdlogic.v"
|
||||
|
||||
module test_mac128pal();
|
||||
// Instantiate individual test modules.
|
||||
test_ls161 tu0();
|
||||
test_ls245 tu1();
|
||||
|
||||
// Perform the remainder of global configuration here.
|
||||
|
||||
// Set simulation time limit.
|
||||
initial begin
|
||||
#480 $finish;
|
||||
end
|
||||
|
||||
// We can use `$display()` for printf-style messages and implement
|
||||
// our own automated test suite that way if we wish.
|
||||
initial begin
|
||||
$display("Example message: Start of simulation. ",
|
||||
"(time == %1.0t)", $time);
|
||||
end
|
||||
|
||||
// Log to a VCD (Variable Change Dump) file.
|
||||
initial begin
|
||||
$dumpfile("test_mac128pal.vcd");
|
||||
$dumpvars;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,107 @@
|
|||
`ifndef TEST_STDLOGIC_V
|
||||
`define TEST_STDLOGIC_V
|
||||
|
||||
`include "stdlogic.v"
|
||||
|
||||
module test_ls161();
|
||||
wire vcc, gnd;
|
||||
reg n_res;
|
||||
reg clock;
|
||||
reg simclk;
|
||||
|
||||
reg n_clr, n_load, enp, ent;
|
||||
reg [3:0] loadvec;
|
||||
wire [3:0] outvec;
|
||||
wire rco;
|
||||
|
||||
assign vcc = 1;
|
||||
assign gnd = 0;
|
||||
|
||||
ls161 u0_ls161(n_clr, clock,
|
||||
loadvec[0], loadvec[1], loadvec[2], loadvec[3],
|
||||
enp, gnd,
|
||||
n_load, ent,
|
||||
outvec[3], outvec[2], outvec[1], outvec[0],
|
||||
rco, vcc);
|
||||
|
||||
// Trigger RESET at beginning of simulation. Make sure there is an
|
||||
// initial falling edge.
|
||||
initial begin
|
||||
n_res = 1;
|
||||
#2 n_res = 0;
|
||||
#18 n_res = 1;
|
||||
end
|
||||
|
||||
// Initialize clock.
|
||||
initial begin
|
||||
clock = 0;
|
||||
simclk = 0;
|
||||
end
|
||||
|
||||
// 10 unit clock cycle.
|
||||
always
|
||||
#5 clock = ~clock;
|
||||
|
||||
// Sub-cycle simulator clock triggers as fast as possible.
|
||||
always
|
||||
#1 simclk = ~simclk;
|
||||
|
||||
// Play with input values a bit.
|
||||
initial begin
|
||||
n_load = 1;
|
||||
n_clr = 1;
|
||||
loadvec = 4'hc;
|
||||
enp = 1;
|
||||
ent = 1;
|
||||
#2 n_clr = 0;
|
||||
#18 n_clr = 1;
|
||||
#222 n_load = 0;
|
||||
#18 n_load = 1;
|
||||
#40 enp = 0;
|
||||
#20 enp = 1;
|
||||
#40 ent = 0;
|
||||
#20 ent = 1;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module test_ls245();
|
||||
wire vcc, gnd;
|
||||
|
||||
reg dir, n_oe, n_oe_a, n_oe_b;
|
||||
reg [7:0] drv_a, drv_b;
|
||||
wire [7:0] sa, sb; // sense_a, sense_b
|
||||
|
||||
assign vcc = 1;
|
||||
assign gnd = 0;
|
||||
|
||||
assign sa = (n_oe_a) ? 8'bz : drv_a;
|
||||
assign sb = (n_oe_b) ? 8'bz : drv_b;
|
||||
|
||||
ls245 u0_ls245(dir, sa[0], sa[1], sa[2], sa[3], sa[4], sa[5],
|
||||
sa[6], sa[7], gnd, sb[7], sb[6], sb[5], sb[4],
|
||||
sb[3], sb[2], sb[1], sb[0], n_oe, vcc);
|
||||
|
||||
// Play with input values a bit.
|
||||
initial begin
|
||||
dir = 0;
|
||||
n_oe = 1;
|
||||
n_oe_a = 1;
|
||||
n_oe_b = 1;
|
||||
drv_a = 0;
|
||||
drv_b = 0;
|
||||
#10 n_oe = 0;
|
||||
#10 n_oe_b = 0;
|
||||
#10 drv_b = 8'hc6;
|
||||
#10 drv_b = 8'h35;
|
||||
#10 n_oe_b = 1; n_oe_a = 0;
|
||||
#10 dir = 1;
|
||||
#10 drv_a = 8'h7f;
|
||||
#10 n_oe_a = 1; n_oe_b = 0; dir = 0;
|
||||
#10 n_oe_a = 0; n_oe_b = 1; dir = 1;
|
||||
#10 n_oe_b = 0; // Test a conflict condition.
|
||||
#10 dir = 0; n_oe_a = 1; // Release the conflict.
|
||||
#10 n_oe = 1; n_oe_a = 1; n_oe_b = 1;
|
||||
end
|
||||
endmodule
|
||||
|
||||
`endif // not TEST_STDLOGIC_V
|
Loading…
Reference in New Issue