103 lines
4.0 KiB
TypeScript
103 lines
4.0 KiB
TypeScript
/**
|
|
* Phase 1 — 4-valued logic + drive-strength resolution (project/multichip-bus/).
|
|
* Covers the exit criteria from 03-phases.md: a shared net with a pull-up and
|
|
* two tri-state drivers resolves to 0/1/Z/X per the rules; contention -> X.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
resolveNet,
|
|
modeToDrive,
|
|
resolvedToBool,
|
|
Strength,
|
|
HIGHZ_DRIVE,
|
|
type Drive,
|
|
} from '../simulation/customChips/busLogic';
|
|
|
|
const strong = (v: 0 | 1): Drive => ({ value: v, strength: Strength.STRONG });
|
|
const pull = (v: 0 | 1): Drive => ({ value: v, strength: Strength.PULL });
|
|
const supply = (v: 0 | 1): Drive => ({ value: v, strength: Strength.SUPPLY });
|
|
|
|
describe('busLogic — resolveNet', () => {
|
|
it('no drivers -> Z (floating)', () => {
|
|
expect(resolveNet([]).v).toBe('Z');
|
|
expect(resolveNet([HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('Z');
|
|
});
|
|
|
|
it('a single strong driver wins', () => {
|
|
expect(resolveNet([strong(1)]).v).toBe('1');
|
|
expect(resolveNet([strong(0)]).v).toBe('0');
|
|
});
|
|
|
|
it('any real driver overrides Z', () => {
|
|
expect(resolveNet([strong(1), HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
|
|
});
|
|
|
|
it('strong beats pull', () => {
|
|
expect(resolveNet([strong(0), pull(1)]).v).toBe('0');
|
|
expect(resolveNet([pull(0), strong(1)]).v).toBe('1');
|
|
});
|
|
|
|
it('a lone pull resistor wins over all-Z (floating bus reads the pull)', () => {
|
|
expect(resolveNet([pull(1), HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
|
|
expect(resolveNet([pull(0)]).v).toBe('0');
|
|
});
|
|
|
|
it('supply beats strong', () => {
|
|
expect(resolveNet([supply(1), strong(0)]).v).toBe('1');
|
|
});
|
|
|
|
it('two strong drivers that agree -> that value (no contention)', () => {
|
|
expect(resolveNet([strong(1), strong(1)]).v).toBe('1');
|
|
});
|
|
|
|
it('two strong drivers that disagree -> X (contention)', () => {
|
|
const r = resolveNet([strong(0), strong(1)]);
|
|
expect(r.v).toBe('X');
|
|
expect(r.strength).toBe(Strength.STRONG);
|
|
});
|
|
|
|
it('reports the winning strength', () => {
|
|
expect(resolveNet([pull(1)]).strength).toBe(Strength.PULL);
|
|
expect(resolveNet([strong(1)]).strength).toBe(Strength.STRONG);
|
|
expect(resolveNet([]).strength).toBe(Strength.HIGHZ);
|
|
});
|
|
|
|
// The Phase 1 exit scenario: a data bus with a pull-up + two tri-state drivers.
|
|
it('tri-state bus: only the enabled driver drives; releasing falls to the pull-up', () => {
|
|
const pullup = pull(1);
|
|
// Driver A enabled to 0, driver B released -> bus reads 0.
|
|
expect(resolveNet([pullup, strong(0), HIGHZ_DRIVE]).v).toBe('0');
|
|
// Both drivers released -> bus floats up to the pull-up -> 1.
|
|
expect(resolveNet([pullup, HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
|
|
// Both drivers enabled to OPPOSITE values -> contention -> X.
|
|
expect(resolveNet([pullup, strong(0), strong(1)]).v).toBe('X');
|
|
});
|
|
});
|
|
|
|
describe('busLogic — modeToDrive', () => {
|
|
it('outputs drive strong at their value', () => {
|
|
expect(modeToDrive(1 /* OUTPUT */, 1)).toEqual({ value: 1, strength: Strength.STRONG });
|
|
expect(modeToDrive(16 /* OUTPUT_LOW */, 0)).toEqual({ value: 0, strength: Strength.STRONG });
|
|
expect(modeToDrive(17 /* OUTPUT_HIGH */, 1)).toEqual({ value: 1, strength: Strength.STRONG });
|
|
});
|
|
|
|
it('plain input / analog release the bus (Hi-Z)', () => {
|
|
expect(modeToDrive(0 /* INPUT */, 1).strength).toBe(Strength.HIGHZ);
|
|
expect(modeToDrive(4 /* ANALOG */, 0).strength).toBe(Strength.HIGHZ);
|
|
});
|
|
|
|
it('pull-up / pull-down contribute a pull-strength level', () => {
|
|
expect(modeToDrive(2 /* INPUT_PULLUP */, 0)).toEqual({ value: 1, strength: Strength.PULL });
|
|
expect(modeToDrive(3 /* INPUT_PULLDOWN */, 1)).toEqual({ value: 0, strength: Strength.PULL });
|
|
});
|
|
});
|
|
|
|
describe('busLogic — resolvedToBool', () => {
|
|
it('only a driven 1 is high; 0/Z/X read low', () => {
|
|
expect(resolvedToBool(resolveNet([strong(1)]))).toBe(true);
|
|
expect(resolvedToBool(resolveNet([strong(0)]))).toBe(false);
|
|
expect(resolvedToBool(resolveNet([]))).toBe(false); // Z
|
|
expect(resolvedToBool(resolveNet([strong(0), strong(1)]))).toBe(false); // X
|
|
});
|
|
});
|