epromsim/main.c

608 lines
18 KiB
C

/***************************************************************************
* Copyright (C) 04/2013 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program 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; version 2 of the License, *
* *
* This program 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 11059200
#include <util/delay.h>
/*
* attiny2313
* lfuse: 0xff (ext. crystal, slow rising power, max startup time)
* hfuse: 0xdb (2.7V BOD)
* efuse: 0xff (self Prog enabled)
*
* PB0 -> NC
* PB1 -> reset-out
* PB2 -> /WR SRAM
* PB3 -> SRCK (shift register clock)
* PB4 -> SER (shift register input)
* PB5 -> CCK (counter clock)
* PB6 -> RCK (counter and shift register store clock)
* PB7 -> /OE (output enable counter, shift register, reset-out, A16)
* PD0 <- RXD
* PD1 -> TXD
* PD2 <- /PowerFail
* PD3 -> A16
* PD4 -> /LED_GN
* PD5 -> /LED_RT
* PD6 -> /OE (output enable RAM and address buffer, counter clear)
*/
#define RESET_OUT PORTB1
#define nRAM_WR PORTB2
#define SREG_CLK PORTB3
#define SREG_DAT PORTB4
#define CNT_CLK PORTB5
#define REG_STORE PORTB6
#define nEMU_EN PORTB7
#define RXD PORTD0
#define TXD PORTD1
#define nPOWERFAIL PORTD2
#define EMU_A16 PORTD3
#define nLED_GN PORTD4
#define nLED_RT PORTD5
#define nTARGET_EN PORTD6
#define BAUDRATE 115200
#define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1)
/* ************************************************************************* */
#define MSGTYPE_VERSION_REQ 0x01 /* no payload */
#define MSGTYPE_PAGESIZE_REQ 0x02 /* no payload */
#define MSGTYPE_CONFIG_REQ 0x03 /* eprom_type(1), pagesize(1), reset_polarity(1) */
#define MSGTYPE_PROGMODE_REQ 0x04 /* progmode(1) */
#define MSGTYPE_SETADDRESS_REQ 0x05 /* address(3) msb first */
#define MSGTYPE_WRITE_REQ 0x06 /* data(0-pagesize) */
#define MSGTYPE_READ_REQ 0x07 /* length(1) */
#define MSGTYPE_ERROR_RSP 0x80 /* error_code(1) */
#define MSGTYPE_VERSION_RSP 0x81 /* version(?) */
#define MSGTYPE_PAGESIZE_RSP 0x82 /* pagesize(1) */
#define MSGTYPE_CONFIG_RSP 0x83 /* no payload */
#define MSGTYPE_PROGMODE_RSP 0x84 /* no payload */
#define MSGTYPE_SETADDRESS_RSP 0x85 /* no payload */
#define MSGTYPE_WRITE_RSP 0x86 /* no payload */
#define MSGTYPE_READ_RSP 0x87 /* data(0-pagesize) */
#define SUCCESS 0x00
#define ERROR_UNKNOWN_COMMAND 0x01 /* unknown message type */
#define ERROR_NOT_SUPPORTED 0x02 /* command not supported */
#define ERROR_INVALID_MODE 0x03 /* invalid progmode */
#define ERROR_INVALID_PARAMETER 0x04 /* invalid parameter in request */
#define ERROR_INVALID_ADDRESS 0x05 /* write outside of configured region */
#define RESET_POLARITY_LOW 0x00 /* low active reset */
#define RESET_POLARITY_HIGH 0x01 /* high active reset */
#define EPROM_TYPE_2K 0x02 /* 2716 */
#define EPROM_TYPE_4K 0x04 /* 2732 */
#define EPROM_TYPE_8K 0x08 /* 2764 */
#define EPROM_TYPE_16K 0x10 /* 27128 */
#define EPROM_TYPE_32K 0x20 /* 27256 */
#define EPROM_TYPE_64K 0x40 /* 27512 */
#define EPROM_TYPE_128K 0x80 /* 27010 */
#define PROGMODE_DISABLED 0x00 /* target running, no write access to RAM */
#define PROGMODE_ENABLED 0x01 /* target reset, write access to RAM */
#define PAGESIZE_MAX 128
/* ************************************************************************* */
struct _globdata {
uint32_t address_max;
uint32_t address;
uint8_t progmode;
uint8_t reset_polarity;
uint8_t pagesize;
uint8_t address_mask;
};
static const uint8_t version_str[] = "epromsim v1.00";
static struct _globdata gdata = { 0 };
/* *************************************************************************
* send one byte to UART
* ************************************************************************* */
static void ser_send(uint8_t data)
{
loop_until_bit_is_set(UCSRA, UDRIE);
UDR = data;
} /* ser_send */
/* *************************************************************************
* receive one byte from UART
* ************************************************************************* */
static uint8_t ser_recv(void)
{
loop_until_bit_is_set(UCSRA, RXC);
return UDR;
} /* ser_recv */
/* *************************************************************************
* shift one byte out to register (LSB first)
* ************************************************************************* */
static void shift_data(uint8_t data)
{
uint8_t mask;
for (mask = 0x01; mask != 0; mask <<= 1)
{
if (data & mask)
{
PORTB |= (1<<SREG_DAT);
}
else
{
PORTB &= ~(1<<SREG_DAT);
}
/* positive edge clocks in data */
PORTB |= (1<<SREG_CLK);
PORTB &= ~(1<<SREG_CLK);
}
} /* shift_data */
/* *************************************************************************
* store pulse for register and counter
* ************************************************************************* */
static void store_pulse(void)
{
/* positive edge transfers data to output buffer */
PORTB |= (1<<REG_STORE);
PORTB &= ~(1<<REG_STORE);
} /* store_pulse */
/* *************************************************************************
* write pulse for SRAM
* ************************************************************************* */
static void write_pulse(void)
{
/* positive edge clocks in data */
PORTB &= ~(1<<nRAM_WR);
PORTB |= (1<<nRAM_WR);
} /* write_pulse */
/* *************************************************************************
* reset address counter
* ************************************************************************* */
static void address_reset(void)
{
PORTD &= ~(1<<nTARGET_EN);
PORTD |= (1<<nTARGET_EN);
} /* address_reset */
/* *************************************************************************
* increment address counter
* ************************************************************************* */
static void address_inc_pulse(void)
{
/* positive edge increments counter */
PORTB |= (1<<CNT_CLK);
PORTB &= ~(1<<CNT_CLK);
} /* address_inc_pulse */
/* *************************************************************************
* controls A16 line
* ************************************************************************* */
static void address_set_a16(uint8_t enable)
{
if (enable)
{
PORTD |= (1<<EMU_A16);
}
else
{
PORTD &= ~(1<<EMU_A16);
}
} /* address_set_a16 */
/* *************************************************************************
* switch access to RAM between emulator and target
* ************************************************************************* */
static void set_progmode(uint8_t progmode)
{
gdata.progmode = progmode;
if (progmode)
{
/* switch RAM access to EMU */
PORTD |= (1<<nTARGET_EN);
PORTD &= ~(1<<nLED_RT);
PORTB &= ~(1<<nEMU_EN);
}
else
{
/* set eprom address line mask */
shift_data(gdata.address_mask);
shift_data(0x00);
store_pulse();
/* switch RAM access to TARGET */
PORTB |= (1<<nEMU_EN);
PORTD &= ~(1<<nTARGET_EN);
PORTD |= (1<<nLED_RT);
/* address counter is reset */
gdata.address = 0x00;
}
/* set RESET_OUT */
if (progmode ^ gdata.reset_polarity)
{
PORTB &= ~(1<<RESET_OUT);
}
else
{
PORTB |= (1<<RESET_OUT);
}
} /* set_progmode */
/* *************************************************************************
* set_address
* ************************************************************************* */
static void set_address(uint32_t address)
{
/* reset address counter */
if (address < gdata.address)
{
address_reset();
gdata.address = 0;
}
// TODO: A16 is not controlled by counter
while (gdata.address++ < address)
{
address_inc_pulse();
}
} /* set_address */
/* *************************************************************************
* write_data
* ************************************************************************* */
static void write_data(uint8_t length)
{
while (length--)
{
address_set_a16(gdata.address++ > 0xFFFF);
shift_data(ser_recv());
store_pulse();
write_pulse();
address_inc_pulse();
}
} /* write_data */
/* *************************************************************************
* fill RAM with 0xFF
* ************************************************************************* */
static void do_clear(void)
{
uint16_t i = 0xFFFF;
shift_data(0xFF);
PORTD &= ~(1<<EMU_A16);
do {
store_pulse();
write_pulse();
address_inc_pulse();
} while (i--);
PORTD |= (1<<EMU_A16);
do {
store_pulse();
write_pulse();
address_inc_pulse();
} while (i--);
} /* do_clear */
/* *************************************************************************
* configures number of addresslines
* ************************************************************************* */
static uint8_t set_eprom_type(uint8_t type)
{
switch (type)
{
case EPROM_TYPE_2K:
gdata.address_max = 0x0800;
gdata.address_mask = 0x00;
break;
case EPROM_TYPE_4K:
gdata.address_max = 0x1000;
gdata.address_mask = 0x80;
break;
case EPROM_TYPE_8K:
gdata.address_max = 0x2000;
gdata.address_mask = 0xC0;
break;
case EPROM_TYPE_16K:
gdata.address_max = 0x4000;
gdata.address_mask = 0xE0;
break;
case EPROM_TYPE_32K:
gdata.address_max = 0x8000;
gdata.address_mask = 0xF0;
break;
case EPROM_TYPE_64K:
gdata.address_max = 0x10000;
gdata.address_mask = 0xF8;
break;
case EPROM_TYPE_128K:
gdata.address_max = 0x20000;
gdata.address_mask = 0xFC;
break;
default:
return ERROR_INVALID_PARAMETER;
}
return SUCCESS;
} /* set_eprom_type */
#if 0
/* Powerfail / Wakeup */
ISR(INT0_vect)
{
if (1) {
/*
* Power Fail:
* - enable RESET_OUT
* - disable nTARGET_EN
* - enable rising edge INT0 (nPOWERFAIL)
* - disable pullups
* - put MCU in standby
*/
do_reset(1);
/* disable green LED */
PORTD |= (1<<nLED_GN);
} else {
/*
* Power Restore:
* - enable pullups
* - enable falling edge INT0 (nPOWERFAIL)
* - enable nTARGET_EN again
* - disable RESET_OUT
*/
/* enable green LED */
PORTD &= ~(1<<nLED_GN);
do_reset(0);
}
}
#endif
int main(void) __attribute__ ((noreturn));
int main(void)
{
DDRB = 0xFF;
PORTB = (1<<nRAM_WR) | (1<<nEMU_EN);
DDRD = ~((1<<RXD) | (1<<nPOWERFAIL));
PORTD = (1<<RXD) | (1<<nPOWERFAIL) | (1<<nLED_GN) | (1<<nLED_RT) | (1<<nTARGET_EN);
/* enable UART 115200,8n1 */
UBRRH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
UCSRB = (1<<RXEN) | (1<<TXEN);
#if 0
/* powerfail: detect falling edge on INT0 */
MCUCR = (1<<ISC01);
GIMSK = (1<<INT0);
sei();
#endif
/* enable LED and reset Counter */
PORTD &= ~((1<<nLED_GN) | (1<<nTARGET_EN));
/* init RAM */
set_eprom_type(EPROM_TYPE_128K);
set_progmode(PROGMODE_ENABLED);
do_clear();
set_progmode(PROGMODE_DISABLED);
while (1)
{
uint8_t msgtype = ser_recv();
uint8_t length = ser_recv();
uint8_t error_code = ERROR_INVALID_PARAMETER;
switch (msgtype)
{
case MSGTYPE_VERSION_REQ:
if (length == 0x00)
{
ser_send(MSGTYPE_VERSION_RSP);
ser_send(sizeof(version_str) -1);
const uint8_t * p_data = version_str;
while (*p_data)
{
ser_send(*p_data++);
}
error_code = SUCCESS;
}
break;
case MSGTYPE_PAGESIZE_REQ:
if (length == 0x00)
{
ser_send(MSGTYPE_PAGESIZE_RSP);
ser_send(0x01);
ser_send(PAGESIZE_MAX);
error_code = SUCCESS;
}
break;
case MSGTYPE_CONFIG_REQ:
if (length == 0x03)
{
uint8_t eprom_type = ser_recv();
uint8_t pagesize = ser_recv();
uint8_t reset_polarity = ser_recv();
if (gdata.progmode == PROGMODE_ENABLED)
{
error_code = ERROR_INVALID_MODE;
}
else
{
if ((reset_polarity <= RESET_POLARITY_HIGH) &&
(pagesize <= PAGESIZE_MAX) &&
(set_eprom_type(eprom_type) == SUCCESS)
)
{
gdata.pagesize = pagesize;
gdata.reset_polarity = reset_polarity;
ser_send(MSGTYPE_CONFIG_RSP);
ser_send(0x00);
error_code = SUCCESS;
}
}
}
break;
case MSGTYPE_PROGMODE_REQ:
if (length == 0x01)
{
uint8_t progmode = ser_recv();
if (progmode <= PROGMODE_ENABLED)
{
set_progmode(progmode);
ser_send(MSGTYPE_PROGMODE_RSP);
ser_send(0x00);
error_code = SUCCESS;
}
}
break;
case MSGTYPE_SETADDRESS_REQ:
if (length == 0x03)
{
uint32_t address;
address = ser_recv();
address = (address << 8) | ser_recv();
address = (address << 8) | ser_recv();
if (address < gdata.address_max)
{
if (gdata.progmode == PROGMODE_DISABLED)
{
error_code = ERROR_INVALID_MODE;
}
else
{
set_address(address);
ser_send(MSGTYPE_SETADDRESS_RSP);
ser_send(0x00);
error_code = SUCCESS;
}
}
}
break;
case MSGTYPE_WRITE_REQ:
if ((length > 0) &&
(length <= gdata.pagesize)
)
{
if ((gdata.address >= gdata.address_max) ||
((gdata.address + length) > gdata.address_max)
)
{
error_code = ERROR_INVALID_ADDRESS;
}
else if (gdata.progmode == PROGMODE_DISABLED)
{
error_code = ERROR_INVALID_MODE;
}
else
{
write_data(length);
ser_send(MSGTYPE_WRITE_RSP);
ser_send(0x00);
error_code = SUCCESS;
}
}
break;
case MSGTYPE_READ_REQ:
error_code = ERROR_NOT_SUPPORTED;
break;
default:
error_code = ERROR_UNKNOWN_COMMAND;
break;
}
if (error_code != SUCCESS)
{
/* read remaining request */
while (length--)
{
(void)ser_recv();
}
ser_send(MSGTYPE_ERROR_RSP);
ser_send(0x01);
ser_send(error_code);
}
}
} /* main */