mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
bios: serial, network and flash boot support
This commit is contained in:
parent
5917048a37
commit
4d754dbb33
10 changed files with 1036 additions and 8 deletions
|
@ -1,7 +1,7 @@
|
||||||
M2DIR=../..
|
M2DIR=../..
|
||||||
include $(M2DIR)/software/include.mak
|
include $(M2DIR)/software/include.mak
|
||||||
|
|
||||||
OBJECTS=crt0.o isr.o ddrinit.o main.o
|
OBJECTS=crt0.o isr.o ddrinit.o timer.o main.o microudp.o tftp.o boot-helper.o boot.o
|
||||||
|
|
||||||
all: bios.bin
|
all: bios.bin
|
||||||
|
|
||||||
|
|
10
software/bios/boot-helper.S
Normal file
10
software/bios/boot-helper.S
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.section .text, "ax", @progbits
|
||||||
|
.global boot_helper
|
||||||
|
boot_helper:
|
||||||
|
/* Invalidate instruction cache */
|
||||||
|
wcsr ICC, r0
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
call r5
|
284
software/bios/boot.c
Normal file
284
software/bios/boot.c
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <console.h>
|
||||||
|
#include <uart.h>
|
||||||
|
#include <system.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <extra/crc.h>
|
||||||
|
#include <sfl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <irq.h>
|
||||||
|
|
||||||
|
#include <hw/flash.h>
|
||||||
|
#include <hw/mem.h>
|
||||||
|
|
||||||
|
#include "timer.h"
|
||||||
|
#include "microudp.h"
|
||||||
|
#include "tftp.h"
|
||||||
|
#include "boot.h"
|
||||||
|
|
||||||
|
extern int rescue;
|
||||||
|
extern void boot_helper(unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int addr);
|
||||||
|
|
||||||
|
static void __attribute__((noreturn)) boot(unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int addr)
|
||||||
|
{
|
||||||
|
printf("Executing booted program.\n");
|
||||||
|
uart_sync();
|
||||||
|
irq_setmask(0);
|
||||||
|
irq_setie(0);
|
||||||
|
boot_helper(r1, r2, r3, r4, addr);
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_ack(void)
|
||||||
|
{
|
||||||
|
int recognized;
|
||||||
|
static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
|
||||||
|
|
||||||
|
timer_enable(0);
|
||||||
|
timer_set_reload(0);
|
||||||
|
timer_set_counter(get_system_frequency()/4);
|
||||||
|
timer_enable(1);
|
||||||
|
recognized = 0;
|
||||||
|
while(timer_get()) {
|
||||||
|
if(uart_read_nonblock()) {
|
||||||
|
char c;
|
||||||
|
c = uart_read();
|
||||||
|
if(c == str[recognized]) {
|
||||||
|
recognized++;
|
||||||
|
if(recognized == SFL_MAGIC_LEN)
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if(c == str[0])
|
||||||
|
recognized = 1;
|
||||||
|
else
|
||||||
|
recognized = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_FAILED 5
|
||||||
|
|
||||||
|
void serialboot(void)
|
||||||
|
{
|
||||||
|
struct sfl_frame frame;
|
||||||
|
int failed;
|
||||||
|
unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
|
||||||
|
static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ;
|
||||||
|
const char *c;
|
||||||
|
|
||||||
|
printf("Booting from serial...\n");
|
||||||
|
|
||||||
|
c = str;
|
||||||
|
while(*c) {
|
||||||
|
uart_write(*c);
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
if(!check_ack()) {
|
||||||
|
printf("Timeout\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
failed = 0;
|
||||||
|
cmdline_adr = initrdstart_adr = initrdend_adr = 0;
|
||||||
|
while(1) {
|
||||||
|
int i;
|
||||||
|
int actualcrc;
|
||||||
|
int goodcrc;
|
||||||
|
|
||||||
|
/* Grab one frame */
|
||||||
|
frame.length = uart_read();
|
||||||
|
frame.crc[0] = uart_read();
|
||||||
|
frame.crc[1] = uart_read();
|
||||||
|
frame.cmd = uart_read();
|
||||||
|
for(i=0;i<frame.length;i++)
|
||||||
|
frame.payload[i] = uart_read();
|
||||||
|
|
||||||
|
/* Check CRC */
|
||||||
|
actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1];
|
||||||
|
goodcrc = crc16(&frame.cmd, frame.length+1);
|
||||||
|
if(actualcrc != goodcrc) {
|
||||||
|
failed++;
|
||||||
|
if(failed == MAX_FAILED) {
|
||||||
|
printf("Too many consecutive errors, aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uart_write(SFL_ACK_CRCERROR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CRC OK */
|
||||||
|
switch(frame.cmd) {
|
||||||
|
case SFL_CMD_ABORT:
|
||||||
|
failed = 0;
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
return;
|
||||||
|
case SFL_CMD_LOAD: {
|
||||||
|
char *writepointer;
|
||||||
|
|
||||||
|
failed = 0;
|
||||||
|
writepointer = (char *)(
|
||||||
|
((unsigned int)frame.payload[0] << 24)
|
||||||
|
|((unsigned int)frame.payload[1] << 16)
|
||||||
|
|((unsigned int)frame.payload[2] << 8)
|
||||||
|
|((unsigned int)frame.payload[3] << 0));
|
||||||
|
for(i=4;i<frame.length;i++)
|
||||||
|
*(writepointer++) = frame.payload[i];
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SFL_CMD_JUMP: {
|
||||||
|
unsigned int addr;
|
||||||
|
|
||||||
|
failed = 0;
|
||||||
|
addr = ((unsigned int)frame.payload[0] << 24)
|
||||||
|
|((unsigned int)frame.payload[1] << 16)
|
||||||
|
|((unsigned int)frame.payload[2] << 8)
|
||||||
|
|((unsigned int)frame.payload[3] << 0);
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
boot(cmdline_adr, initrdstart_adr, initrdend_adr, rescue, addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SFL_CMD_CMDLINE:
|
||||||
|
failed = 0;
|
||||||
|
cmdline_adr = ((unsigned int)frame.payload[0] << 24)
|
||||||
|
|((unsigned int)frame.payload[1] << 16)
|
||||||
|
|((unsigned int)frame.payload[2] << 8)
|
||||||
|
|((unsigned int)frame.payload[3] << 0);
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
break;
|
||||||
|
case SFL_CMD_INITRDSTART:
|
||||||
|
failed = 0;
|
||||||
|
initrdstart_adr = ((unsigned int)frame.payload[0] << 24)
|
||||||
|
|((unsigned int)frame.payload[1] << 16)
|
||||||
|
|((unsigned int)frame.payload[2] << 8)
|
||||||
|
|((unsigned int)frame.payload[3] << 0);
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
break;
|
||||||
|
case SFL_CMD_INITRDEND:
|
||||||
|
failed = 0;
|
||||||
|
initrdend_adr = ((unsigned int)frame.payload[0] << 24)
|
||||||
|
|((unsigned int)frame.payload[1] << 16)
|
||||||
|
|((unsigned int)frame.payload[2] << 8)
|
||||||
|
|((unsigned int)frame.payload[3] << 0);
|
||||||
|
uart_write(SFL_ACK_SUCCESS);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
failed++;
|
||||||
|
if(failed == MAX_FAILED) {
|
||||||
|
printf("Too many consecutive errors, aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uart_write(SFL_ACK_UNKNOWN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOCALIP1 192
|
||||||
|
#define LOCALIP2 168
|
||||||
|
#define LOCALIP3 0
|
||||||
|
#define LOCALIP4 42
|
||||||
|
#define REMOTEIP1 192
|
||||||
|
#define REMOTEIP2 168
|
||||||
|
#define REMOTEIP3 0
|
||||||
|
#define REMOTEIP4 14
|
||||||
|
|
||||||
|
static int tftp_get_v(unsigned int ip, const char *filename, char *buffer)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = tftp_get(ip, filename, buffer);
|
||||||
|
if(r > 0)
|
||||||
|
printf("Successfully downloaded %d bytes from %s over TFTP\n", r, filename);
|
||||||
|
else
|
||||||
|
printf("Unable to download %s over TFTP\n", filename);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void netboot(void)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
|
||||||
|
unsigned int ip;
|
||||||
|
unsigned char *macadr = (unsigned char *)FLASH_OFFSET_MAC_ADDRESS;
|
||||||
|
|
||||||
|
printf("Booting from network...\n");
|
||||||
|
printf("Local IP : %d.%d.%d.%d\n", LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4);
|
||||||
|
printf("Remote IP: %d.%d.%d.%d\n", REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4);
|
||||||
|
|
||||||
|
ip = IPTOINT(REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4);
|
||||||
|
|
||||||
|
microudp_start(macadr, IPTOINT(LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4));
|
||||||
|
|
||||||
|
if(tftp_get_v(ip, "boot.bin", (void *)SDRAM_BASE) <= 0) {
|
||||||
|
printf("Network boot failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdline_adr = SDRAM_BASE+0x1000000;
|
||||||
|
size = tftp_get_v(ip, "cmdline.txt", (void *)cmdline_adr);
|
||||||
|
if(size <= 0) {
|
||||||
|
printf("No command line parameters found\n");
|
||||||
|
cmdline_adr = 0;
|
||||||
|
} else
|
||||||
|
*((char *)(cmdline_adr+size)) = 0x00;
|
||||||
|
|
||||||
|
initrdstart_adr = SDRAM_BASE+0x1002000;
|
||||||
|
size = tftp_get_v(ip, "initrd.bin", (void *)initrdstart_adr);
|
||||||
|
if(size <= 0) {
|
||||||
|
printf("No initial ramdisk found\n");
|
||||||
|
initrdstart_adr = 0;
|
||||||
|
initrdend_adr = 0;
|
||||||
|
} else
|
||||||
|
initrdend_adr = initrdstart_adr + size;
|
||||||
|
|
||||||
|
boot(cmdline_adr, initrdstart_adr, initrdend_adr, rescue, SDRAM_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flashboot(void)
|
||||||
|
{
|
||||||
|
unsigned int *flashbase;
|
||||||
|
unsigned int length;
|
||||||
|
unsigned int crc;
|
||||||
|
unsigned int got_crc;
|
||||||
|
|
||||||
|
printf("Booting from flash...\n");
|
||||||
|
if(rescue)
|
||||||
|
flashbase = (unsigned int *)FLASH_OFFSET_RESCUE_APP;
|
||||||
|
else
|
||||||
|
flashbase = (unsigned int *)FLASH_OFFSET_REGULAR_APP;
|
||||||
|
length = *flashbase++;
|
||||||
|
crc = *flashbase++;
|
||||||
|
if((length < 32) || (length > 4*1024*1024)) {
|
||||||
|
printf("Error: Invalid flash boot image length\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Loading %d bytes from flash...\n", length);
|
||||||
|
memcpy((void *)SDRAM_BASE, flashbase, length);
|
||||||
|
got_crc = crc32((unsigned char *)SDRAM_BASE, length);
|
||||||
|
if(crc != got_crc) {
|
||||||
|
printf("CRC failed (expected %08x, got %08x)\n", crc, got_crc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boot(0, 0, 0, rescue, SDRAM_BASE);
|
||||||
|
}
|
25
software/bios/boot.h
Normal file
25
software/bios/boot.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2012 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BOOT_H
|
||||||
|
#define __BOOT_H
|
||||||
|
|
||||||
|
void serialboot(void);
|
||||||
|
void netboot(void);
|
||||||
|
void flashboot(void);
|
||||||
|
|
||||||
|
#endif /* __BOOT_H */
|
|
@ -26,8 +26,11 @@
|
||||||
#include <extra/crc.h>
|
#include <extra/crc.h>
|
||||||
|
|
||||||
#include <hw/flash.h>
|
#include <hw/flash.h>
|
||||||
|
#include <hw/minimac.h>
|
||||||
|
|
||||||
#include "ddrinit.h"
|
#include "ddrinit.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "boot.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CSR_IE = 1, CSR_IM, CSR_IP, CSR_ICC, CSR_DCC, CSR_CC, CSR_CFG, CSR_EBA,
|
CSR_IE = 1, CSR_IM, CSR_IP, CSR_ICC, CSR_DCC, CSR_CC, CSR_CFG, CSR_EBA,
|
||||||
|
@ -311,9 +314,10 @@ static void help(void)
|
||||||
puts("crc - compute CRC32 of a part of the address space");
|
puts("crc - compute CRC32 of a part of the address space");
|
||||||
puts("rcsr - read processor CSR");
|
puts("rcsr - read processor CSR");
|
||||||
puts("wcsr - write processor CSR");
|
puts("wcsr - write processor CSR");
|
||||||
|
puts("netboot - boot via TFTP");
|
||||||
|
puts("serialboot - boot via SFL");
|
||||||
|
puts("flashboot - boot from flash");
|
||||||
puts("version - display version");
|
puts("version - display version");
|
||||||
puts("reboot - system reset");
|
|
||||||
puts("reconf - reload FPGA configuration");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_token(char **str)
|
static char *get_token(char **str)
|
||||||
|
@ -343,6 +347,10 @@ static void do_command(char *c)
|
||||||
else if(strcmp(token, "mc") == 0) mc(get_token(&c), get_token(&c), get_token(&c));
|
else if(strcmp(token, "mc") == 0) mc(get_token(&c), get_token(&c), get_token(&c));
|
||||||
else if(strcmp(token, "crc") == 0) crc(get_token(&c), get_token(&c));
|
else if(strcmp(token, "crc") == 0) crc(get_token(&c), get_token(&c));
|
||||||
|
|
||||||
|
else if(strcmp(token, "flashboot") == 0) flashboot();
|
||||||
|
else if(strcmp(token, "serialboot") == 0) serialboot();
|
||||||
|
else if(strcmp(token, "netboot") == 0) netboot();
|
||||||
|
|
||||||
else if(strcmp(token, "version") == 0) puts(VERSION);
|
else if(strcmp(token, "version") == 0) puts(VERSION);
|
||||||
|
|
||||||
else if(strcmp(token, "help") == 0) help();
|
else if(strcmp(token, "help") == 0) help();
|
||||||
|
@ -390,6 +398,17 @@ static void crcbios(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ethreset(void)
|
||||||
|
{
|
||||||
|
CSR_MINIMAC_PHYRST = 0;
|
||||||
|
busy_wait(2);
|
||||||
|
/* that pesky ethernet PHY needs two resets at times... */
|
||||||
|
CSR_MINIMAC_PHYRST = 1;
|
||||||
|
busy_wait(2);
|
||||||
|
CSR_MINIMAC_PHYRST = 0;
|
||||||
|
busy_wait(2);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_mac(void)
|
static void print_mac(void)
|
||||||
{
|
{
|
||||||
unsigned char *macadr = (unsigned char *)FLASH_OFFSET_MAC_ADDRESS;
|
unsigned char *macadr = (unsigned char *)FLASH_OFFSET_MAC_ADDRESS;
|
||||||
|
@ -437,6 +456,54 @@ static void readstr(char *s, int size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_user_abort(void)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
|
||||||
|
printf("Automatic boot in 2 seconds...\n");
|
||||||
|
printf("Q/ESC: abort boot\n");
|
||||||
|
printf("F7: boot from serial\n");
|
||||||
|
printf("F8: boot from network\n");
|
||||||
|
timer_enable(0);
|
||||||
|
timer_set_reload(0);
|
||||||
|
timer_set_counter(get_system_frequency()*2);
|
||||||
|
timer_enable(1);
|
||||||
|
while(timer_get()) {
|
||||||
|
if(readchar_nonblock()) {
|
||||||
|
c = readchar();
|
||||||
|
if((c == 'Q')||(c == '\e')) {
|
||||||
|
puts("Aborted");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(c == 0x06) {
|
||||||
|
serialboot();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(c == 0x07) {
|
||||||
|
netboot();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boot_sequence(void)
|
||||||
|
{
|
||||||
|
if(test_user_abort()) {
|
||||||
|
if(rescue) {
|
||||||
|
netboot();
|
||||||
|
serialboot();
|
||||||
|
flashboot();
|
||||||
|
} else {
|
||||||
|
flashboot();
|
||||||
|
netboot();
|
||||||
|
serialboot();
|
||||||
|
}
|
||||||
|
printf("No boot medium found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int i, char **c)
|
int main(int i, char **c)
|
||||||
{
|
{
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
@ -452,14 +519,13 @@ int main(int i, char **c)
|
||||||
if(rescue)
|
if(rescue)
|
||||||
printf("Rescue mode\n");
|
printf("Rescue mode\n");
|
||||||
board_init();
|
board_init();
|
||||||
|
ethreset();
|
||||||
print_mac();
|
print_mac();
|
||||||
ddr_ok = ddrinit();
|
ddr_ok = ddrinit();
|
||||||
if(ddr_ok) {
|
if(ddr_ok)
|
||||||
printf("Booting...\n");
|
boot_sequence();
|
||||||
} else {
|
else
|
||||||
printf("Memory initialization failed\n");
|
printf("Memory initialization failed\n");
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
putsnonl("\e[1mBIOS>\e[0m ");
|
putsnonl("\e[1mBIOS>\e[0m ");
|
||||||
|
|
400
software/bios/microudp.c
Normal file
400
software/bios/microudp.c
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <system.h>
|
||||||
|
#include <extra/crc.h>
|
||||||
|
#include <hw/minimac.h>
|
||||||
|
|
||||||
|
#include "microudp.h"
|
||||||
|
|
||||||
|
#define ETHERTYPE_ARP 0x0806
|
||||||
|
#define ETHERTYPE_IP 0x0800
|
||||||
|
|
||||||
|
struct ethernet_header {
|
||||||
|
unsigned char preamble[8];
|
||||||
|
unsigned char destmac[6];
|
||||||
|
unsigned char srcmac[6];
|
||||||
|
unsigned short ethertype;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0;i<7;i++)
|
||||||
|
h->preamble[i] = 0x55;
|
||||||
|
h->preamble[7] = 0xd5;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
h->destmac[i] = destmac[i];
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
h->srcmac[i] = srcmac[i];
|
||||||
|
h->ethertype = ethertype;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARP_HWTYPE_ETHERNET 0x0001
|
||||||
|
#define ARP_PROTO_IP 0x0800
|
||||||
|
|
||||||
|
#define ARP_OPCODE_REQUEST 0x0001
|
||||||
|
#define ARP_OPCODE_REPLY 0x0002
|
||||||
|
|
||||||
|
struct arp_frame {
|
||||||
|
unsigned short hwtype;
|
||||||
|
unsigned short proto;
|
||||||
|
unsigned char hwsize;
|
||||||
|
unsigned char protosize;
|
||||||
|
unsigned short opcode;
|
||||||
|
unsigned char sender_mac[6];
|
||||||
|
unsigned int sender_ip;
|
||||||
|
unsigned char target_mac[6];
|
||||||
|
unsigned int target_ip;
|
||||||
|
unsigned char padding[18];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define IP_IPV4 0x45
|
||||||
|
#define IP_DONT_FRAGMENT 0x4000
|
||||||
|
#define IP_TTL 64
|
||||||
|
#define IP_PROTO_UDP 0x11
|
||||||
|
|
||||||
|
struct ip_header {
|
||||||
|
unsigned char version;
|
||||||
|
unsigned char diff_services;
|
||||||
|
unsigned short total_length;
|
||||||
|
unsigned short identification;
|
||||||
|
unsigned short fragment_offset;
|
||||||
|
unsigned char ttl;
|
||||||
|
unsigned char proto;
|
||||||
|
unsigned short checksum;
|
||||||
|
unsigned int src_ip;
|
||||||
|
unsigned int dst_ip;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct udp_header {
|
||||||
|
unsigned short src_port;
|
||||||
|
unsigned short dst_port;
|
||||||
|
unsigned short length;
|
||||||
|
unsigned short checksum;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct udp_frame {
|
||||||
|
struct ip_header ip;
|
||||||
|
struct udp_header udp;
|
||||||
|
char payload[];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ethernet_frame {
|
||||||
|
struct ethernet_header eth_header;
|
||||||
|
union {
|
||||||
|
struct arp_frame arp;
|
||||||
|
struct udp_frame udp;
|
||||||
|
} contents;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct ethernet_frame frame;
|
||||||
|
unsigned char raw[1532];
|
||||||
|
} ethernet_buffer;
|
||||||
|
|
||||||
|
|
||||||
|
static int rxlen;
|
||||||
|
static ethernet_buffer *rxbuffer;
|
||||||
|
static ethernet_buffer *rxbuffer0;
|
||||||
|
static ethernet_buffer *rxbuffer1;
|
||||||
|
static int txlen;
|
||||||
|
static ethernet_buffer *txbuffer;
|
||||||
|
|
||||||
|
static void send_packet(void)
|
||||||
|
{
|
||||||
|
unsigned int crc;
|
||||||
|
|
||||||
|
crc = crc32(&txbuffer->raw[8], txlen-8);
|
||||||
|
txbuffer->raw[txlen ] = (crc & 0xff);
|
||||||
|
txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8;
|
||||||
|
txbuffer->raw[txlen+2] = (crc & 0xff0000) >> 16;
|
||||||
|
txbuffer->raw[txlen+3] = (crc & 0xff000000) >> 24;
|
||||||
|
txlen += 4;
|
||||||
|
CSR_MINIMAC_TXCOUNTH = (txlen & 0xff00) >> 8;
|
||||||
|
CSR_MINIMAC_TXCOUNTL = txlen & 0x00ff;
|
||||||
|
CSR_MINIMAC_TXSTART = 1;
|
||||||
|
while(!(CSR_MINIMAC_EV_PENDING & MINIMAC_EV_TX));
|
||||||
|
CSR_MINIMAC_EV_PENDING = MINIMAC_EV_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char my_mac[6];
|
||||||
|
static unsigned int my_ip;
|
||||||
|
|
||||||
|
/* ARP cache - one entry only */
|
||||||
|
static unsigned char cached_mac[6];
|
||||||
|
static unsigned int cached_ip;
|
||||||
|
|
||||||
|
static void process_arp(void)
|
||||||
|
{
|
||||||
|
if(rxlen < 68) return;
|
||||||
|
if(rxbuffer->frame.contents.arp.hwtype != ARP_HWTYPE_ETHERNET) return;
|
||||||
|
if(rxbuffer->frame.contents.arp.proto != ARP_PROTO_IP) return;
|
||||||
|
if(rxbuffer->frame.contents.arp.hwsize != 6) return;
|
||||||
|
if(rxbuffer->frame.contents.arp.protosize != 4) return;
|
||||||
|
if(rxbuffer->frame.contents.arp.opcode == ARP_OPCODE_REPLY) {
|
||||||
|
if(rxbuffer->frame.contents.arp.sender_ip == cached_ip) {
|
||||||
|
int i;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
cached_mac[i] = rxbuffer->frame.contents.arp.sender_mac[i];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(rxbuffer->frame.contents.arp.opcode == ARP_OPCODE_REQUEST) {
|
||||||
|
if(rxbuffer->frame.contents.arp.target_ip == my_ip) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fill_eth_header(&txbuffer->frame.eth_header,
|
||||||
|
rxbuffer->frame.contents.arp.sender_mac,
|
||||||
|
my_mac,
|
||||||
|
ETHERTYPE_ARP);
|
||||||
|
txlen = 68;
|
||||||
|
txbuffer->frame.contents.arp.hwtype = ARP_HWTYPE_ETHERNET;
|
||||||
|
txbuffer->frame.contents.arp.proto = ARP_PROTO_IP;
|
||||||
|
txbuffer->frame.contents.arp.hwsize = 6;
|
||||||
|
txbuffer->frame.contents.arp.protosize = 4;
|
||||||
|
txbuffer->frame.contents.arp.opcode = ARP_OPCODE_REPLY;
|
||||||
|
txbuffer->frame.contents.arp.sender_ip = my_ip;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
txbuffer->frame.contents.arp.sender_mac[i] = my_mac[i];
|
||||||
|
txbuffer->frame.contents.arp.target_ip = rxbuffer->frame.contents.arp.sender_ip;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
txbuffer->frame.contents.arp.target_mac[i] = rxbuffer->frame.contents.arp.sender_mac[i];
|
||||||
|
send_packet();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
|
||||||
|
int microudp_arp_resolve(unsigned int ip)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int tries;
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
if(cached_ip == ip) {
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
if(cached_mac[i]) return 1;
|
||||||
|
}
|
||||||
|
cached_ip = ip;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
cached_mac[i] = 0;
|
||||||
|
|
||||||
|
for(tries=0;tries<5;tries++) {
|
||||||
|
/* Send an ARP request */
|
||||||
|
fill_eth_header(&txbuffer->frame.eth_header,
|
||||||
|
broadcast,
|
||||||
|
my_mac,
|
||||||
|
ETHERTYPE_ARP);
|
||||||
|
txlen = 68;
|
||||||
|
txbuffer->frame.contents.arp.hwtype = ARP_HWTYPE_ETHERNET;
|
||||||
|
txbuffer->frame.contents.arp.proto = ARP_PROTO_IP;
|
||||||
|
txbuffer->frame.contents.arp.hwsize = 6;
|
||||||
|
txbuffer->frame.contents.arp.protosize = 4;
|
||||||
|
txbuffer->frame.contents.arp.opcode = ARP_OPCODE_REQUEST;
|
||||||
|
txbuffer->frame.contents.arp.sender_ip = my_ip;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
txbuffer->frame.contents.arp.sender_mac[i] = my_mac[i];
|
||||||
|
txbuffer->frame.contents.arp.target_ip = ip;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
txbuffer->frame.contents.arp.target_mac[i] = 0;
|
||||||
|
send_packet();
|
||||||
|
|
||||||
|
/* Do we get a reply ? */
|
||||||
|
for(timeout=0;timeout<2000000;timeout++) {
|
||||||
|
microudp_service();
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
if(cached_mac[i]) return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete)
|
||||||
|
{
|
||||||
|
unsigned char *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ptr = (unsigned char *)buffer;
|
||||||
|
length >>= 1;
|
||||||
|
|
||||||
|
for(i=0;i<length;i++)
|
||||||
|
r += ((unsigned int)(ptr[2*i]) << 8)|(unsigned int)(ptr[2*i+1]) ;
|
||||||
|
|
||||||
|
/* Add overflows */
|
||||||
|
while(r >> 16)
|
||||||
|
r = (r & 0xffff) + (r >> 16);
|
||||||
|
|
||||||
|
if(complete) {
|
||||||
|
r = ~r;
|
||||||
|
r &= 0xffff;
|
||||||
|
if(r == 0) r = 0xffff;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *microudp_get_tx_buffer(void)
|
||||||
|
{
|
||||||
|
return txbuffer->frame.contents.udp.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pseudo_header {
|
||||||
|
unsigned int src_ip;
|
||||||
|
unsigned int dst_ip;
|
||||||
|
unsigned char zero;
|
||||||
|
unsigned char proto;
|
||||||
|
unsigned short length;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length)
|
||||||
|
{
|
||||||
|
struct pseudo_header h;
|
||||||
|
unsigned int r;
|
||||||
|
|
||||||
|
if((cached_mac[0] == 0) && (cached_mac[1] == 0) && (cached_mac[2] == 0)
|
||||||
|
&& (cached_mac[3] == 0) && (cached_mac[4] == 0) && (cached_mac[5] == 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
txlen = length + sizeof(struct ethernet_header) + sizeof(struct udp_frame) + 8;
|
||||||
|
if(txlen < 72) txlen = 72;
|
||||||
|
|
||||||
|
fill_eth_header(&txbuffer->frame.eth_header,
|
||||||
|
cached_mac,
|
||||||
|
my_mac,
|
||||||
|
ETHERTYPE_IP);
|
||||||
|
|
||||||
|
txbuffer->frame.contents.udp.ip.version = IP_IPV4;
|
||||||
|
txbuffer->frame.contents.udp.ip.diff_services = 0;
|
||||||
|
txbuffer->frame.contents.udp.ip.total_length = length + sizeof(struct udp_frame);
|
||||||
|
txbuffer->frame.contents.udp.ip.identification = 0;
|
||||||
|
txbuffer->frame.contents.udp.ip.fragment_offset = IP_DONT_FRAGMENT;
|
||||||
|
txbuffer->frame.contents.udp.ip.ttl = IP_TTL;
|
||||||
|
h.proto = txbuffer->frame.contents.udp.ip.proto = IP_PROTO_UDP;
|
||||||
|
txbuffer->frame.contents.udp.ip.checksum = 0;
|
||||||
|
h.src_ip = txbuffer->frame.contents.udp.ip.src_ip = my_ip;
|
||||||
|
h.dst_ip = txbuffer->frame.contents.udp.ip.dst_ip = cached_ip;
|
||||||
|
txbuffer->frame.contents.udp.ip.checksum = ip_checksum(0, &txbuffer->frame.contents.udp.ip,
|
||||||
|
sizeof(struct ip_header), 1);
|
||||||
|
|
||||||
|
txbuffer->frame.contents.udp.udp.src_port = src_port;
|
||||||
|
txbuffer->frame.contents.udp.udp.dst_port = dst_port;
|
||||||
|
h.length = txbuffer->frame.contents.udp.udp.length = length + sizeof(struct udp_header);
|
||||||
|
txbuffer->frame.contents.udp.udp.checksum = 0;
|
||||||
|
|
||||||
|
h.zero = 0;
|
||||||
|
r = ip_checksum(0, &h, sizeof(struct pseudo_header), 0);
|
||||||
|
if(length & 1) {
|
||||||
|
txbuffer->frame.contents.udp.payload[length] = 0;
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
r = ip_checksum(r, &txbuffer->frame.contents.udp.udp,
|
||||||
|
sizeof(struct udp_header)+length, 1);
|
||||||
|
txbuffer->frame.contents.udp.udp.checksum = r;
|
||||||
|
|
||||||
|
send_packet();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static udp_callback rx_callback;
|
||||||
|
|
||||||
|
static void process_ip(void)
|
||||||
|
{
|
||||||
|
if(rxlen < (sizeof(struct ethernet_header)+sizeof(struct udp_frame))) return;
|
||||||
|
/* We don't verify UDP and IP checksums and rely on the Ethernet checksum solely */
|
||||||
|
if(rxbuffer->frame.contents.udp.ip.version != IP_IPV4) return;
|
||||||
|
// check disabled for QEMU compatibility
|
||||||
|
//if(rxbuffer->frame.contents.udp.ip.diff_services != 0) return;
|
||||||
|
if(rxbuffer->frame.contents.udp.ip.total_length < sizeof(struct udp_frame)) return;
|
||||||
|
// check disabled for QEMU compatibility
|
||||||
|
//if(rxbuffer->frame.contents.udp.ip.fragment_offset != IP_DONT_FRAGMENT) return;
|
||||||
|
if(rxbuffer->frame.contents.udp.ip.proto != IP_PROTO_UDP) return;
|
||||||
|
if(rxbuffer->frame.contents.udp.ip.dst_ip != my_ip) return;
|
||||||
|
if(rxbuffer->frame.contents.udp.udp.length < sizeof(struct udp_header)) return;
|
||||||
|
|
||||||
|
if(rx_callback)
|
||||||
|
rx_callback(rxbuffer->frame.contents.udp.ip.src_ip, rxbuffer->frame.contents.udp.udp.src_port, rxbuffer->frame.contents.udp.udp.dst_port, rxbuffer->frame.contents.udp.payload, rxbuffer->frame.contents.udp.udp.length-sizeof(struct udp_header));
|
||||||
|
}
|
||||||
|
|
||||||
|
void microudp_set_callback(udp_callback callback)
|
||||||
|
{
|
||||||
|
rx_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_frame(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int received_crc;
|
||||||
|
unsigned int computed_crc;
|
||||||
|
|
||||||
|
flush_cpu_dcache();
|
||||||
|
for(i=0;i<7;i++)
|
||||||
|
if(rxbuffer->frame.eth_header.preamble[i] != 0x55) return;
|
||||||
|
if(rxbuffer->frame.eth_header.preamble[7] != 0xd5) return;
|
||||||
|
received_crc = ((unsigned int)rxbuffer->raw[rxlen-1] << 24)
|
||||||
|
|((unsigned int)rxbuffer->raw[rxlen-2] << 16)
|
||||||
|
|((unsigned int)rxbuffer->raw[rxlen-3] << 8)
|
||||||
|
|((unsigned int)rxbuffer->raw[rxlen-4]);
|
||||||
|
computed_crc = crc32(&rxbuffer->raw[8], rxlen-12);
|
||||||
|
if(received_crc != computed_crc) return;
|
||||||
|
|
||||||
|
rxlen -= 4; /* strip CRC here to be consistent with TX */
|
||||||
|
if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_ARP) process_arp();
|
||||||
|
else if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_IP) process_ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void microudp_start(unsigned char *macaddr, unsigned int ip)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
CSR_MINIMAC_EV_PENDING = MINIMAC_EV_RX0 | MINIMAC_EV_RX1 | MINIMAC_EV_TX;
|
||||||
|
|
||||||
|
rxbuffer0 = (ethernet_buffer *)MINIMAC_RX0_BASE;
|
||||||
|
rxbuffer1 = (ethernet_buffer *)MINIMAC_RX1_BASE;
|
||||||
|
txbuffer = (ethernet_buffer *)MINIMAC_TX_BASE;
|
||||||
|
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
my_mac[i] = macaddr[i];
|
||||||
|
my_ip = ip;
|
||||||
|
|
||||||
|
cached_ip = 0;
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
cached_mac[i] = 0;
|
||||||
|
|
||||||
|
rx_callback = (udp_callback)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void microudp_service(void)
|
||||||
|
{
|
||||||
|
if(CSR_MINIMAC_EV_PENDING & MINIMAC_EV_RX0) {
|
||||||
|
rxlen = (CSR_MINIMAC_RXCOUNT0H << 8) | CSR_MINIMAC_RXCOUNT0L;
|
||||||
|
rxbuffer = rxbuffer0;
|
||||||
|
process_frame();
|
||||||
|
CSR_MINIMAC_EV_PENDING = MINIMAC_EV_RX0;
|
||||||
|
}
|
||||||
|
if(CSR_MINIMAC_EV_PENDING & MINIMAC_EV_RX1) {
|
||||||
|
rxlen = (CSR_MINIMAC_RXCOUNT1H << 8) | CSR_MINIMAC_RXCOUNT1L;
|
||||||
|
rxbuffer = rxbuffer1;
|
||||||
|
process_frame();
|
||||||
|
CSR_MINIMAC_EV_PENDING = MINIMAC_EV_RX1;
|
||||||
|
}
|
||||||
|
}
|
34
software/bios/microudp.h
Normal file
34
software/bios/microudp.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_MICROUDP_H
|
||||||
|
#define __NET_MICROUDP_H
|
||||||
|
|
||||||
|
#define IPTOINT(a, b, c, d) ((a << 24)|(b << 16)|(c << 8)|d)
|
||||||
|
|
||||||
|
#define MICROUDP_BUFSIZE (5*1532)
|
||||||
|
|
||||||
|
typedef void (*udp_callback)(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *data, unsigned int length);
|
||||||
|
|
||||||
|
void microudp_start(unsigned char *macaddr, unsigned int ip);
|
||||||
|
int microudp_arp_resolve(unsigned int ip);
|
||||||
|
void *microudp_get_tx_buffer(void);
|
||||||
|
int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length);
|
||||||
|
void microudp_set_callback(udp_callback callback);
|
||||||
|
void microudp_service(void);
|
||||||
|
|
||||||
|
#endif /* __NET_MICROUDP_H */
|
136
software/bios/tftp.c
Normal file
136
software/bios/tftp.c
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2012 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "microudp.h"
|
||||||
|
#include "tftp.h"
|
||||||
|
|
||||||
|
#define PORT_OUT 69
|
||||||
|
#define PORT_IN 7642
|
||||||
|
|
||||||
|
static int format_request(char *buf, const char *filename)
|
||||||
|
{
|
||||||
|
*buf++ = 0x00; /* Opcode: Request */
|
||||||
|
*buf++ = 0x01;
|
||||||
|
strcpy(buf, filename);
|
||||||
|
buf += strlen(filename);
|
||||||
|
*buf++ = 0x00;
|
||||||
|
*buf++ = 'o';
|
||||||
|
*buf++ = 'c';
|
||||||
|
*buf++ = 't';
|
||||||
|
*buf++ = 'e';
|
||||||
|
*buf++ = 't';
|
||||||
|
*buf++ = 0x00;
|
||||||
|
return 9+strlen(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int format_ack(char *buf, unsigned short block)
|
||||||
|
{
|
||||||
|
*buf++ = 0x00; /* Opcode: Ack */
|
||||||
|
*buf++ = 0x04;
|
||||||
|
*buf++ = (block & 0xff00) >> 8;
|
||||||
|
*buf++ = (block & 0x00ff);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *packet_data;
|
||||||
|
static int total_length;
|
||||||
|
static int transfer_finished;
|
||||||
|
static char *dst_buffer;
|
||||||
|
|
||||||
|
static void rx_callback(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *_data, unsigned int length)
|
||||||
|
{
|
||||||
|
unsigned char *data = (unsigned char *)_data;
|
||||||
|
unsigned short opcode;
|
||||||
|
unsigned short block;
|
||||||
|
int i;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
if(length < 4) return;
|
||||||
|
if(dst_port != PORT_IN) return;
|
||||||
|
opcode = ((unsigned short)(data[0]) << 8)|((unsigned short)(data[1]));
|
||||||
|
block = ((unsigned short)(data[2]) << 8)|((unsigned short)(data[3]));
|
||||||
|
if(block < 1) return;
|
||||||
|
if(opcode == 3) { /* Data */
|
||||||
|
length -= 4;
|
||||||
|
offset = (block-1)*512;
|
||||||
|
for(i=0;i<length;i++)
|
||||||
|
dst_buffer[offset+i] = data[i+4];
|
||||||
|
total_length += length;
|
||||||
|
if(length < 512)
|
||||||
|
transfer_finished = 1;
|
||||||
|
|
||||||
|
length = format_ack(packet_data, block);
|
||||||
|
microudp_send(PORT_IN, src_port, length);
|
||||||
|
}
|
||||||
|
if(opcode == 5) { /* Error */
|
||||||
|
total_length = -1;
|
||||||
|
transfer_finished = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tftp_get(unsigned int ip, const char *filename, char *buffer)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
int tries;
|
||||||
|
int i;
|
||||||
|
int length_before;
|
||||||
|
|
||||||
|
if(!microudp_arp_resolve(ip))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
microudp_set_callback(rx_callback);
|
||||||
|
|
||||||
|
packet_data = microudp_get_tx_buffer();
|
||||||
|
dst_buffer = buffer;
|
||||||
|
|
||||||
|
total_length = 0;
|
||||||
|
transfer_finished = 0;
|
||||||
|
tries = 5;
|
||||||
|
while(1) {
|
||||||
|
len = format_request(packet_data, filename);
|
||||||
|
microudp_send(PORT_IN, PORT_OUT, len);
|
||||||
|
for(i=0;i<2000000;i++) {
|
||||||
|
microudp_service();
|
||||||
|
if((total_length > 0) || transfer_finished) break;
|
||||||
|
}
|
||||||
|
if((total_length > 0) || transfer_finished) break;
|
||||||
|
tries--;
|
||||||
|
if(tries == 0) {
|
||||||
|
microudp_set_callback(NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length_before = total_length;
|
||||||
|
while(!transfer_finished) {
|
||||||
|
if(length_before != total_length) {
|
||||||
|
i = 12000000;
|
||||||
|
length_before = total_length;
|
||||||
|
}
|
||||||
|
if(i-- == 0) {
|
||||||
|
microudp_set_callback(NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
microudp_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
microudp_set_callback(NULL);
|
||||||
|
|
||||||
|
return total_length;
|
||||||
|
}
|
24
software/bios/tftp.h
Normal file
24
software/bios/tftp.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_TFTP_H
|
||||||
|
#define __NET_TFTP_H
|
||||||
|
|
||||||
|
int tftp_get(unsigned int ip, const char *filename, char *buffer);
|
||||||
|
|
||||||
|
#endif /* __NET_TFTP_H */
|
||||||
|
|
49
software/include/hw/minimac.h
Normal file
49
software/include/hw/minimac.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Milkymist SoC (Software)
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2010, 2012 Sebastien Bourdeauducq
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __HW_MINIMAC_H
|
||||||
|
#define __HW_MINIMAC_H
|
||||||
|
|
||||||
|
#include <hw/common.h>
|
||||||
|
#include <csrbase.h>
|
||||||
|
|
||||||
|
#define MINIMAC_CSR(x) MMPTR(MINIMAC_BASE+(x))
|
||||||
|
|
||||||
|
#define CSR_MINIMAC_PHYRST MINIMAC_CSR(0x00)
|
||||||
|
|
||||||
|
#define CSR_MINIMAC_RXCOUNT0H MINIMAC_CSR(0x04)
|
||||||
|
#define CSR_MINIMAC_RXCOUNT0L MINIMAC_CSR(0x08)
|
||||||
|
#define CSR_MINIMAC_RXCOUNT1H MINIMAC_CSR(0x0C)
|
||||||
|
#define CSR_MINIMAC_RXCOUNT1L MINIMAC_CSR(0x10)
|
||||||
|
|
||||||
|
#define CSR_MINIMAC_TXCOUNTH MINIMAC_CSR(0x14)
|
||||||
|
#define CSR_MINIMAC_TXCOUNTL MINIMAC_CSR(0x18)
|
||||||
|
#define CSR_MINIMAC_TXSTART MINIMAC_CSR(0x1C)
|
||||||
|
|
||||||
|
#define CSR_MINIMAC_EV_STAT MINIMAC_CSR(0x20)
|
||||||
|
#define CSR_MINIMAC_EV_PENDING MINIMAC_CSR(0x24)
|
||||||
|
#define CSR_MINIMAC_EV_ENABLE MINIMAC_CSR(0x28)
|
||||||
|
|
||||||
|
#define MINIMAC_EV_RX0 0x1
|
||||||
|
#define MINIMAC_EV_RX1 0x2
|
||||||
|
#define MINIMAC_EV_TX 0x4
|
||||||
|
|
||||||
|
#define MINIMAC_RX0_BASE 0xb0000000
|
||||||
|
#define MINIMAC_RX1_BASE 0xb0000800
|
||||||
|
#define MINIMAC_TX_BASE 0xb0001000
|
||||||
|
|
||||||
|
#endif /* __HW_MINIMAC_H */
|
Loading…
Reference in a new issue