From: Sebastien Bourdeauducq Date: Mon, 21 May 2012 20:57:12 +0000 (+0200) Subject: bios: serial, network and flash boot support X-Git-Tag: 24jan2021_ls180~3179 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4d754dbb33cd28b3ee6a31976b6f7c3b2e5ed1b1;p=litex.git bios: serial, network and flash boot support --- diff --git a/software/bios/Makefile b/software/bios/Makefile index e7f00154..1410b63d 100644 --- a/software/bios/Makefile +++ b/software/bios/Makefile @@ -1,7 +1,7 @@ M2DIR=../.. 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 diff --git a/software/bios/boot-helper.S b/software/bios/boot-helper.S new file mode 100644 index 00000000..da99182a --- /dev/null +++ b/software/bios/boot-helper.S @@ -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 diff --git a/software/bios/boot.c b/software/bios/boot.c new file mode 100644 index 00000000..f4227e31 --- /dev/null +++ b/software/bios/boot.c @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 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); +} diff --git a/software/bios/boot.h b/software/bios/boot.h new file mode 100644 index 00000000..58a33f55 --- /dev/null +++ b/software/bios/boot.h @@ -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 . + */ + +#ifndef __BOOT_H +#define __BOOT_H + +void serialboot(void); +void netboot(void); +void flashboot(void); + +#endif /* __BOOT_H */ diff --git a/software/bios/main.c b/software/bios/main.c index 4d1eaafe..dc5a3e20 100644 --- a/software/bios/main.c +++ b/software/bios/main.c @@ -26,8 +26,11 @@ #include #include +#include #include "ddrinit.h" +#include "timer.h" +#include "boot.h" enum { 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("rcsr - read 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("reboot - system reset"); - puts("reconf - reload FPGA configuration"); } 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, "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, "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) { 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) { char buffer[64]; @@ -452,14 +519,13 @@ int main(int i, char **c) if(rescue) printf("Rescue mode\n"); board_init(); + ethreset(); print_mac(); ddr_ok = ddrinit(); - if(ddr_ok) { - printf("Booting...\n"); - } else { + if(ddr_ok) + boot_sequence(); + else printf("Memory initialization failed\n"); - } - while(1) { putsnonl("\e[1mBIOS>\e[0m "); diff --git a/software/bios/microudp.c b/software/bios/microudp.c new file mode 100644 index 00000000..c80c60ba --- /dev/null +++ b/software/bios/microudp.c @@ -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 . + */ + +#include +#include +#include +#include + +#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> 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; + } +} diff --git a/software/bios/microudp.h b/software/bios/microudp.h new file mode 100644 index 00000000..46cac233 --- /dev/null +++ b/software/bios/microudp.h @@ -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 . + */ + +#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 */ diff --git a/software/bios/tftp.c b/software/bios/tftp.c new file mode 100644 index 00000000..d155b9fc --- /dev/null +++ b/software/bios/tftp.c @@ -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 . + */ + +#include + +#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 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; +} diff --git a/software/bios/tftp.h b/software/bios/tftp.h new file mode 100644 index 00000000..27609332 --- /dev/null +++ b/software/bios/tftp.h @@ -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 . + */ + +#ifndef __NET_TFTP_H +#define __NET_TFTP_H + +int tftp_get(unsigned int ip, const char *filename, char *buffer); + +#endif /* __NET_TFTP_H */ + diff --git a/software/include/hw/minimac.h b/software/include/hw/minimac.h new file mode 100644 index 00000000..c9c9d4d1 --- /dev/null +++ b/software/include/hw/minimac.h @@ -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 . + */ + +#ifndef __HW_MINIMAC_H +#define __HW_MINIMAC_H + +#include +#include + +#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 */