file: hc11spi.vhd
--
-- Motorola HC11 VHDL model
-- Copyright (C) Green Mountain Computing Systems, 1995
-- All rights reserved.
--
-- This software is provided "as is" without warranty of any kind. Green
-- Mountain Computing Systems does not accept any responsibility for results
-- obtained by using this software and does not guarantee that the software
-- is correct.
--
-- hc11spi.vhd : This is the VHDL behavioral implementation of the HC11's
-- Serial Peripheral Interface (SPI)
--
-- 5/25/95 : Create - Scott Thibault
--
-- Uses vector functions and the HC11 types.
library vector,hc11type;
use vector.functions.all;
use hc11type.types.all;
entity spi is
port (E : in bit;
ph1, ph2, reset : in bit;
ss: in bit; -- External SS pin
sckin: in bit; -- SCK input for slave from master
sckout: out bit; -- SCK generated by master
mi,si : in bit; -- Slave's serial I/O
mo,so : out bit; -- Master's serial I/O
ss_dir : in bit; -- Direction of SS I/O pin
mfault : out bit; -- Active high, when mfault has occurred
int_spi : out bit; -- SPI's interrupt request line
spe,slave : out bit; -- SPI state used in external pin logic
-- System bus signals
reg_en : in bit;
as, bus_rw : in bit;
bus_addr : in word;
bus_data : inout databus bus);
-- Bit positions of various SPI related flags in HC11 control registers
constant SPIE : integer:=7;
constant SPE1 : integer:=6;
constant DWOM : integer:=5;
constant MSTR : integer:=4;
constant CPOL : integer:=3;
constant CPHA : integer:=2;
constant SPR1 : integer:=1;
constant SPR0 : integer:=0;
constant SPIF : integer:=7;
constant WCOL : integer:=6;
constant MODF : integer:=4;
-- States of a state machine that control the SPI
constant WAIT_ST : integer:=0;
constant TRAN_ST : integer:=1;
constant RECV_ST : integer:=2;
constant DELAY1_ST : integer:=3;
constant DELAY2_ST : integer:=4;
constant DELAY3_ST : integer:=5;
end spi;
architecture behavoir of spi is
-- SPI registers
signal SPCR,SPSR,SPDR_shift : byte;
-- Control registers to set SPSR flags, and SPDR value read by the CPU
signal SPDR_read: byte;
signal set_SPIF,set_MODF : bit; -- Controls signals for SPSR register
signal SPDR_full : bit; -- Flag set when the SPDR is loaded
signal SPSR_read: byte; -- Value of SPSR, when last read
-- Clock divider block signals to generate different transfer rates
signal divided : bit_vector (4 downto 0);
signal prescaled : bit_vector (3 downto 0);
-- Miscellaneous internal signals
signal clock,spi_clk,bit_in,bit_out,shift_edge,load : bit;
signal state,no_bits,clk_sel : integer:=0;
signal found_start,shift: boolean;
-- Bus signal values latched during valid address portion of bus cycle
signal address : natural;
signal rw,wr_enable,rd_enable : bit;
begin
sckout<=clock; -- Master's clock drives the sck output
mfault<=SPSR(MODF); -- Single mode faults to pin buffers
-- The spe and slave signals make the current mode of the SPI available to
-- other parts of the HC11 model.
spe<=SPCR(SPE1);
slave<=not SPCR(MSTR);
-- Inputs and outputs are reversed for master and slave
bit_in<=mi when SPCR(MSTR)='1' else si; -- Select correct input
process (bit_out,SPCR) -- Select correct output
begin
if (SPCR(MSTR)='1') then
mo<=bit_out;
else
so<=bit_out;
end if;
end process;
-- Generate interrupt, if enabled, when a trasfer is complete (SPIF='1')
int_spi<=(SPSR(SPIF) or SPSR(MODF)) and SPCR(SPIE);
-- Generate divided signals from ph2
divider: process (ph2)
begin
if (ph2='1' and ph2'event) then
divided<=divided+"00001";
end if;
end process;
-- Pick out clocks used from /2 chain
prescaled(0)<=divided(0);
prescaled(1)<=divided(1);
prescaled(2)<=divided(3);
prescaled(3)<=divided(4);
-- Master clock is derived from clock selected by SPR1,SPR0 from the 4
-- prescaled clocks.
spi_clk<=prescaled(to_natural(SPCR(SPR1 downto SPR0)));
-- Generate clock for shift regsiter, only active during transmit or receive
main_clock: process (state,spi_clk,sckin)
begin
if (state=TRAN_ST) then
-- If master, then use internally generated clock
clock<=spi_clk;
elsif (state=RECV_ST) then
-- If slave, then use sck input connected to masters clock
clock<=sckin;
else
-- If idle, then clock is inactive (level determined by clock polarity)
clock<=SPCR(CPOL);
end if;
end process;
-- The edge which causes the shift depends on CPOL and CPHA
shift_edge<=SPCR(CPOL) xor SPCR(CPHA);
-- Count the number bits that have been shifted
count: process(clock,state)
begin
if (state=WAIT_ST) then
no_bits<=0;
else
if (clock=shift_edge and clock'event) then
no_bits<=no_bits+1 after 1ns;
end if;
end if;
end process;
-- Output is just the 7th bit of the shift register or SPDR
bit_out<=SPDR_shift(7);
-- Detect edges for shifts. This is complicated because when CPHA='1'
-- the first edge must be ignored and there is no final edge
process (clock,SPCR,SPSR)
begin
if (clock=shift_edge and clock'event) then
if (SPCR(CPHA)='0' or no_bits/=0) then
shift<=true;
else
shift<=false;
end if;
elsif (SPSR(SPIF)='1' and SPSR(SPIF)'event) then
shift<=true;
else
shift<=false;
end if;
end process;
-- Maintain the shift register part of SPDR
do_shift: process (shift,E)
begin
if (state/=WAIT_ST) then
SPDR_full<='0';
end if;
if (E='0' and E'event) then
if (wr_enable='1' and address=16#2A#) then
-- CPU write to the SPDR
SPDR_shift<=bus_data;
SPDR_full<='1';
end if;
end if;
if (shift and shift'event) then
-- Transfer next bit
for i in byte'high downto byte'low+1 loop
SPDR_shift(i)<=SPDR_shift(i-1);
end loop;
SPDR_shift(0)<=bit_in;
end if;
end process;
-- Search for start of a receive for slave, this depends on the CPHA mode
search_start: process(state,SPCR,ss,sckin)
begin
if (reset='0' or state/=WAIT_ST) then
found_start<=false;
else
if (SPCR(CPHA)='0') then
found_start<= ss='0' and ss'event;
else
found_start<= sckin/=SPCR(CPOL) and sckin'event;
end if;
end if;
end process;
-- A finite state machine to control the SPI
fsm: process(reset,ph2)
variable tmp : byte;
begin
set_SPIF<='0';
if (reset='0') then
state<=WAIT_ST;
elsif (ph2='0' and ph2'event) then
case state is
when WAIT_ST => -- waiting
if (SPCR(MSTR)='1') then
-- Master starts transmiting when SPDR is written
if (SPDR_full='1' and spi_clk=SPCR(CPOL)) then
state<=TRAN_ST;
end if;
else
-- Slave starts receiving after edge on ss or sck as detected by
-- by the search_start process.
if (found_start) then
state<=RECV_ST;
end if;
end if;
when TRAN_ST =>
-- Transfer complete, if 8 bits have been sent
if ((no_bits=8) and (clock=SPCR(CPOL))) then
if (SPCR(CPHA)='0') then
state<=WAIT_ST;
SPDR_read<=SPDR_shift;
set_SPIF<='1';
else
state<=DELAY1_ST;
end if;
end if;
when RECV_ST =>
-- Transfer complete, if 8 bits have been read
if ((no_bits=8) and (clock=SPCR(CPOL))) then
if (SPCR(CPHA)='0') then
state<=DELAY3_ST;
elsif (spi_clk=SPCR(CPOL)) then
state<=DELAY2_ST;
end if;
end if;
when DELAY1_ST =>
if (spi_clk/=SPCR(CPOL)) then
state<=WAIT_ST;
SPDR_read<=SPDR_shift;
set_SPIF<='1';
end if;
when DELAY2_ST =>
if (spi_clk/=SPCR(CPOL)) then
state<=DELAY3_ST;
end if;
when DELAY3_ST =>
if (spi_clk=SPCR(CPOL)) then
state<=WAIT_ST;
SPDR_read<=SPDR_shift;
set_SPIF<='1';
end if;
end case;
end if;
end process;
-- Accept CPU writes to the SPCR control register
SPCR_register: process (E,set_MODF)
begin
if (set_MODF='1' and set_MODF'event) then
SPCR(MSTR)<='0';
elsif (E='0' and E'event) then
if (wr_enable='1' and address=16#28#) then
SPCR<=bus_data;
end if;
end if;
end process;
-- Accept CPU writes to the SPSR register, and set flags in the SPSR
-- based on the SPSR_set control signals.
SPSR_register: process (reset,E,set_SPIF,set_MODF)
begin
if (E='0' and E'event) then
if (wr_enable='1' and address=16#29#) then
SPSR<=bus_data;
elsif (wr_enable='1' and address=16#28# and SPSR_read(MODF)='1') then
SPSR(MODF)<='0'; -- MODF clearing sequence
end if;
end if;
if (set_SPIF='1' and set_SPIF'event) then
SPSR(SPIF)<='1';
end if;
if (set_MODF='1' and set_MODF'event) then
SPSR(MODF)<='1';
end if;
if (reset='0') then
SPSR<=X"00";
end if;
end process;
-- Detect mode faults
process (SPCR,ss_dir,ss)
begin
-- If configured as master, mode and ss are configured for input
-- and if ss goes low, then there is a mode-fault error.
if (SPCR(MSTR)='1' and ss_dir='0' and ss='0') then
set_MODF<='1';
else
set_MODF<='0';
end if;
end process;
-- Monitor system bus cycles for reads of SPI regsiters
bus_cycle: process
begin
-- Wait for address strobe
wait until as='0';
-- Latch read/write enable and address signals
rd_enable<=reg_en and bus_rw;
wr_enable<=reg_en and not bus_rw;
-- use natural for address to speed up simulation (faster comarisons)
address<=to_natural(bus_addr(7 downto 0)); -- low byte of address
-- Wait for start data valid
wait until ph1='1';
if (rd_enable='1' and reset='1') then
-- If reading that drive bus with register data
case address is
when 16#28# =>
bus_data<=SPCR;
when 16#29# =>
bus_data<=SPSR;
SPSR_read<=SPSR;
when 16#2A# =>
bus_data<=SPDR_read;
when others =>
-- Ignore read of non-SPI register
null;
end case;
wait until ph1='0';
bus_data<=null; -- Remove driver at end of data valid
end if;
end process;
end behavoir;
type b to return to text