Shop OBEX P1 Docs P2 Docs Learn Events
Idea: Propeddle with the P1V and a 6502 Softcore — Parallax Forums

Idea: Propeddle with the P1V and a 6502 Softcore

65816581 Posts: 132
edited 2014-10-08 22:58 in Propeller 1
Hi,

I stumbled upon the Propeddle Project from jacgoudsmit and maybe it would be cool to port it to pure
FPGA? Buffers, 128K RAM, P1V, 6502 (T65 Core) and Octal D Flipflop can all be done in the FPGA (e.g.
on the BeMicroCVs Cyclone V).

@jacgoudsmith: What do you think?

Comments

  • pik33pik33 Posts: 2,366
    edited 2014-10-01 09:49
    Adding a 6502 to the Propeller is a good idea which opens all kind of retrocomputing things.

    I cannot find a Propeddle schematic in readable format (pdf...) - so I don't know in details how are they connected i it.
  • 65816581 Posts: 132
    edited 2014-10-01 15:29
    pik33 wrote: »
    Adding a 6502 to the Propeller is a good idea which opens all kind of retrocomputing things.

    I cannot find a Propeddle schematic in readable format (pdf...) - so I don't know in details how are they connected i it.

    Here you go: https://github.com/jacgoudsmit/Propeddle/blob/master/Hardware/Kicad/Plot/Schematic.pdf?raw=true
  • mklrobomklrobo Posts: 420
    edited 2014-10-01 16:37
    :cool: Cool Idea! :cool:
    A unquie concept that opens up a world of possibilities! It begs the question,
    what other processes can be masked on the FPGA?
  • mklrobomklrobo Posts: 420
    edited 2014-10-01 16:40
    Would it be possible, (conversion unknown), I wonder, to mask organic logic
    on the FPGA? Just a thought..........:)
  • jac_goudsmitjac_goudsmit Posts: 418
    edited 2014-10-01 21:04
    6581 wrote: »
    Hi,

    I stumbled upon the Propeddle Project from jacgoudsmit and maybe it would be cool to port it to pure
    FPGA? Buffers, 128K RAM, P1V, 6502 (T65 Core) and Octal D Flipflop can all be done in the FPGA (e.g.
    on the BeMicroCVs Cyclone V).

    @jacgoudsmith: What do you think?

    Of course I've thought about this but let's think about this for a minute.

    First of all, with the P1V code, the Cyclone 5 that's on the BeMicro CV is almost full. Adding a soft-core 6502 to the P1V certainly wouldn't fit in the FPGA. You would need a bigger FPGA such as the on on the DE2-115.

    On the Propeddle, the Propeller is intended as an easy-to-program emulator for the peripherals that would normally be in a 6502 computer. Because of the 8 cogs and the large amount of available software in OBEX, it's relatively easy to emulate an Apple 1 and it should be pretty easy to emulate simple 6502 systems such as KIM-1, SYM, PET-2001, OSI etc, or to create your own 6502 system using Spin or PASM or C or C++. For many hobbyists, this makes it possible to put the kit together and make it do something useful. But the Propeller can "only" do 8 things at once, at a speed that's relatively slow because everything has to happen sequentially with PASM instructions. The Apple 1 emulator uses 4 cogs and it has almost not hardware that's (emulated to be) directly connected to the 6502.

    An FPGA can do a much better job of emulating hardware, including complicated things such as video. The big advantage of an FPGA is that it basically does everything at the same time, just like normal digital electronics.

    So, if you want to emulate a 6502 system with all its peripherals, it makes much more sense to just put the 6502 softcore in the FPGA and emulate the peripherals with Verilog directly. It will take a lot less resources than emulating a Propeller and a 6502 but of course not everyone knows Verilog.

    And of course this has been done before: on the 6502.org forums and website, there are several people who used an FPGA to emulate old systems and implement new systems.

    ===Jac
  • 65816581 Posts: 132
    edited 2014-10-02 02:49
    Of course I've thought about this but let's think about this for a minute.

    So, if you want to emulate a 6502 system with all its peripherals, it makes much more sense to just put the 6502 softcore in the FPGA and emulate the peripherals with Verilog directly. It will take a lot less resources than emulating a Propeller and a 6502 but of course not everyone knows Verilog.

    And of course this has been done before: on the 6502.org forums and website, there are several people who used an FPGA to emulate old systems and implement new systems.

    ===Jac

    I agree with that, but I was thinking about a 6 core P1V as it seems to give enough space for the 6502 and the "glue logic".
    By the way - I ported this to the BeMicro and currently working on some 6502/6510 code + MOS6581, so we have a stand-alone
    sid player: https://git.no-route.org/syso/bemicro-cv-multicomp
  • porcupineporcupine Posts: 80
    edited 2014-10-02 07:07
    It makes more sense to me to build out the Propeller P1V as its own 'home computer' type platform; take out a COG or two for space, add HUBEXEC & external/expanded HUB ram, and add dedicated FPGA video, sound, I/O circuity, freeing up the cogs to do 'other' stuff. Combine that with VGA or HDMI output, SD card reader, and a simple OS that boots to BASIC or Lua or something, and you have a fun little machine much like what many of us grew up on -- but faster, open source, extensible, etc. The Prop instruction set is better than the old 6502, and you can emulate a C64, etc. much easier without involving the prop.
  • pik33pik33 Posts: 2,366
    edited 2014-10-02 08:50
    Making my vga driver with SRAM controller I started to do such kind of thing. I want to add a display list and some sprites to it. I have a working de2-115 soundchip and a Propeller sid/wav player working, so the next thing will be to add the sound chip to the system. Then I have an SPI SDHC controller running @ 50 MHz which can be added too.

    Having a soft 6502 core will enable a POKEY/SID players which can play not only SID dumps but real sid files.

    Then, of course, SD and Basic.

    I was Atari 8-bit user so all my work is inspired by this machine.The pallette I made for my "mode 2" is Atari 8-bit like. The difference is - this is 448x280 @ 256 colors instead 320x200 @ 2 colors. Or... 1792x1120 @ 4 colors. With switching pallette banks and sprites added it can make a new level of retrocomputing.

    And.. adding a 6502 core to the system may enable Atari 8-bit software to run...
  • 65816581 Posts: 132
    edited 2014-10-02 12:47
    pik33 wrote: »
    Making my vga driver with SRAM controller I started to do such kind of thing. I want to add a display list and some sprites to it. I have a working de2-115 soundchip and a Propeller sid/wav player working, so the next thing will be to add the sound chip to the system. Then I have an SPI SDHC controller running @ 50 MHz which can be added too.

    Having a soft 6502 core will enable a POKEY/SID players which can play not only SID dumps but real sid files.

    Then, of course, SD and Basic.

    I was Atari 8-bit user so all my work is inspired by this machine.The pallette I made for my "mode 2" is Atari 8-bit like. The difference is - this is 448x280 @ 256 colors instead 320x200 @ 2 colors. Or... 1792x1120 @ 4 colors. With switching pallette banks and sprites added it can make a new level of retrocomputing.

    And.. adding a 6502 core to the system may enable Atari 8-bit software to run...

    Do you already have your code at Git? If not, would be cool if you could maintain it at GitHub (or if you want something
    different, I can set it up at my GitLab server)
  • pik33pik33 Posts: 2,366
    edited 2014-10-02 13:34
    I don't have my code at Git, I don't knw how to use git and maintain the code with it. The code (player and vga) is published here; the vga is still alpha - I did some cleaning of the spin code today; it was very messy so I couldn't go further. The verilog code started to going to the beta stage: it compiles every time and seems to work stable, but there is still a lot to do with it.
  • AleAle Posts: 2,363
    edited 2014-10-07 09:57
    I am working on a SD Driver, just verilog :). If someone wants to try it, drop me a line. It sort of works but needs a bit more debugging :):
    /**
     * Buffered SD-Driver
     * Data is transferred from-to a 512 bytes buffer for easy/simple access
     *
     * (c) Alejandro Paz Schmidt, distributed under the terms of the MIT license
     */
    
    
    module sd_driver(
        input wire          clk_in,
        
        
        // sd interface
        
        output wire         sd_clk,
        output wire         sd_cs_n,
        output wire         sd_data_o,
        input wire          sd_data_in,
        
        // cpu interface
        input wire          cpu_clk_in,     // memory read/write clock
    `ifdef ALTERA_CV
        input wire [8:0]   cpu_addr_in,
    `else
        input wire [10:0]   cpu_addr_in,
    `endif
        input wire          cpu_oe_in,
        input wire          cpu_we_in,
    	 
    `ifdef ALTERA_CV
        input wire [31:0]    cpu_data_in,
        output wire [31:0]   cpu_data_o,
    `else
        input wire [7:0]    cpu_data_in,
        output wire [7:0]   cpu_data_o,
    `endif    
        output wire         wled_o,         // activity led write
        output wire         rled_o          // activity led read
        
        );
        
    parameter ST_RESET              = 0;
    parameter ST_WAIT_FOR_CMD       = 1;
    parameter ST_ACK_CMD_RECEIVE    = 2;
    parameter ST_ACK_CMD_TRANSMIT   = 3;
    parameter ST_TRANSFER           = 4;
    parameter ST_TRANSFER_START     = 5;
    parameter ST_SEND_BITS          = 6;
    parameter ST_SEND_BITS_END      = 7;
    `ifdef ALTERA_CV
    parameter BUFFER_BIT           = 7; // bit of the address that determines which buffer is being written/read
    parameter DATA_BIT             = 8; // 0 means control register, 1 means data buffers
    parameter DATA_BUS_WIDTH		 = 32;
    `else
    
    parameter BUFFER_BIT           = 9; // bit of the address that determines which buffer is being written/read
    parameter DATA_BIT             = 10; // 0 means control register, 1 means data buffers
    parameter DATA_BUS_WIDTH		 = 8;
    `endif
    // ctrl reg 0
    parameter CMD_RECV_BIT          = 7;
    parameter CMD_SEND_BIT          = 6;
    parameter CMD_CS_BEG_BIT        = 5;
    parameter CMD_CS_END_BIT        = 4;
    parameter CMD_USE_CRC           = 3;
    // ctrl reg 2
    parameter CMD_BUFF_BIT          = 7;
    parameter CMD_FULL_PAGE         = 6;
    
    reg [7:0] sd_cmd_reg_0, sd_cmd_reg_1, sd_cmd_reg_2;
    
    reg [2:0] sd_state;
    reg [8:0] sd_tfr_size;
    reg [3:0] sd_bit;
    reg [8:0] sd_buff_addr;
    reg sd_buffer; // asserted when data buffer
    reg sd_inhibit_sdo;    // when asserted sends '0' to the SD card, used for sending commands
    reg sd_inhibit_write;  // when asserted inhibits writes to the buffer from the card side
    reg [7:0] sd_shift_reg_in, sd_shift_reg_o;
    reg sd_mem_write;
    reg [2:0] sd_bit_cnt;
    reg sd_cs;
    reg [7:0] crc7;
    reg [7:0] latched_data;
    reg sd_cmd_acknowledged; // asserted when a command is in progress, inhibits writes to the command register
    reg sd_cmd_reg_dirty; // command register dirty, needs processing
    
    wire [7:0] sd_mem_in, sd_mem_o;
    
    wire [7:0] new_crc_7, xor_data_crc, new_crc_0, new_crc_1, new_crc_2, new_crc_3, new_crc_4;
    wire [7:0] new_crc_5, new_crc_6;
    
    wire [DATA_BUS_WIDTH-1:0] cpu_data_from_buffer;
    
    
    // command register 0
    // 7    receive bytes from card
    // 6    send to card from buffer
    // 5    CS status at the begin of the transmission
    // 4    CS status at the end of the transaction
    // 3    use crc instead of last byte of command
    // 2..0 not used
    
    // command register 1
    // 7..0 not yet used
    // 
    // 
    // 
    // 
    // 
    
    // command register 2, transfer size
    // 7    buffer 0 or 1
    // 6    full page transfer
    // 5..0 size in bytes + 1    
    
    // command register 3 clear crc register
    
    
    assign sd_data_o = sd_inhibit_sdo ? 1'b0:sd_shift_reg_o[7];
    
    assign sd_clk = (sd_inhibit_sdo ? /* normal clock */ clk_in:!clk_in) & (sd_state == ST_SEND_BITS);
    assign sd_cs_n = sd_cs;
    
    `ifdef ALTERA_CV
    assign cpu_data_o = (cpu_oe_in & (!cpu_addr_in[DATA_BIT])) ? { cpu_addr_in, 15'h0, sd_cmd_acknowledged }:cpu_data_from_buffer;
    `else
    assign cpu_data_o = (cpu_oe_in & (!cpu_addr_in[DATA_BIT])) ? { 7'h0, sd_cmd_acknowledged }:cpu_data_from_buffer;
    `endif
    assign rled_o = !sd_cs_n & sd_inhibit_sdo;
    assign wled_o = !sd_cs_n & !sd_inhibit_sdo;
    
    assign new_crc_7 = ( cpu_data_in[7] ^      crc7[6]) ? {      crc7[6:0], 1'b0 }^8'h09:{      crc7[6:0], 1'b0 };
    assign new_crc_6 = ( cpu_data_in[6] ^ new_crc_7[5]) ? { new_crc_7[6:0], 1'b0 }^8'h09:{ new_crc_7[6:0], 1'b0 };
    assign new_crc_5 = ( cpu_data_in[5] ^ new_crc_6[4]) ? { new_crc_6[6:0], 1'b0 }^8'h09:{ new_crc_6[6:0], 1'b0 };
    assign new_crc_4 = ( cpu_data_in[4] ^ new_crc_5[3]) ? { new_crc_5[6:0], 1'b0 }^8'h09:{ new_crc_5[6:0], 1'b0 };
    assign new_crc_3 = ( cpu_data_in[3] ^ new_crc_4[2]) ? { new_crc_4[6:0], 1'b0 }^8'h09:{ new_crc_4[6:0], 1'b0 };
    assign new_crc_2 = ( cpu_data_in[2] ^ new_crc_3[1]) ? { new_crc_3[6:0], 1'b0 }^8'h09:{ new_crc_3[6:0], 1'b0 };
    assign new_crc_1 = ( cpu_data_in[1] ^ new_crc_2[0]) ? { new_crc_2[6:0], 1'b0 }^8'h09:{ new_crc_2[6:0], 1'b0 };
    assign new_crc_0 = ( cpu_data_in[0]               ) ? { new_crc_1[6:0], 1'b0 }^8'h09:{ new_crc_1[6:0], 1'b0 };
    
    
    `ifdef SIMULATOR
    ram_dp_1k ram_1k(
        .clka(clk_in),
        .addra( { sd_buffer, sd_buff_addr[8:0] }),
        .writea(sd_mem_write),
        .reada(1'b1),
        .dataa(sd_shift_reg_in),
        .qa(sd_mem_o),
        .clkb(cpu_clk_in),
        .addrb(cpu_addr_in[9:0]),
        .writeb(cpu_we_in & cpu_addr_in[DATA_BIT]),
        .readb(cpu_oe_in & cpu_addr_in[DATA_BIT]),
        .datab(cpu_data_in),
        .qb(cpu_data_from_buffer)
        );
    `else
    `ifdef ALTERA_CV
    wire [31:0] sd_mem_data;
    spi_buffer ram_1k(
    
    	.address_a( { sd_buffer, sd_buff_addr[8:2] }),
    	.data_a( { sd_shift_reg_in, sd_shift_reg_in, sd_shift_reg_in, sd_shift_reg_in } ),
    	.wren_a(sd_mem_write),
    	.byteena_a(sd_buff_addr[1:0] == 2'h0 ? 4'b0001:
                             sd_buff_addr[1:0] == 2'h1 ? 4'b0010:
                             sd_buff_addr[1:0] == 2'h2 ? 4'b0100:4'b1000),
    	.clock_a(clk_in),
    	.q_a(sd_mem_data),
    	
    	.clock_b(cpu_clk_in),
    	.address_b(cpu_addr_in[7:0]),
    	.data_b(cpu_data_in),
    	.wren_b(cpu_we_in & cpu_addr_in[DATA_BIT]),
    	.byteena_b(4'hf),
    	.q_b(cpu_data_from_buffer));
    
    assign sd_mem_o = sd_buff_addr[1:0] == 2'h0 ? sd_mem_data[7:0]:
                             sd_buff_addr[1:0] == 2'h1 ? sd_mem_data[15:8]:
                             sd_buff_addr[1:0] == 2'h2 ? sd_mem_data[23:16]:sd_mem_data[31:24];
    
    `else
    // Lattice True dual ported memory 1 Kbytes 8 bits
    spi_buffer ram_1k(
    	.DataInA(sd_shift_reg_in), 
    	.AddressA( { sd_buffer, sd_buff_addr[8:0] }), 
    	.ClockA(clk_in), 
    	.ClockEnA(1'b1), 
    	.WrA(sd_mem_write), 
    	.ResetA(1'b0), 
    	.QA(sd_mem_o), 
    	.DataInB(cpu_data_in), 
    	.AddressB(cpu_addr_in[9:0]), 
    	.ClockB(cpu_clk_in), 
        .ClockEnB(1'b1), 
    	.WrB(cpu_we_in & cpu_addr_in[DATA_BIT]), 
    	.ResetB(1'b0), 
    	.QB(cpu_data_from_buffer)
    	);
    `endif // ALTERA_CV
    `endif
    
    always @(posedge clk_in)
        begin
            if (cpu_we_in)
                begin
                     if (cpu_addr_in[DATA_BIT]) // write to control register
                        begin
                            crc7 <= new_crc_0; // save last crc
                            latched_data <= cpu_addr_in; // recalculates crc
                        end
                    else
                        case (cpu_addr_in[1:0])
                            2'h0:
                                if ((!sd_cmd_acknowledged) && (!sd_cmd_reg_dirty))
                                    begin
                                        sd_cmd_reg_0 <= cpu_data_in;
                                        sd_cmd_reg_dirty <= 1'b1;
                                    end
                            2'h1: 
                                begin
                                    sd_cmd_reg_1 <= cpu_data_in;
                                end
                            2'h2: // transfer size
                                begin
                                    sd_cmd_reg_2 <= cpu_data_in;
                                    
                                end
                            2'h3: crc7 <= 8'h0; // clear crc register
                        endcase
                end
            
            if (sd_cmd_acknowledged && sd_cmd_reg_dirty)
                begin
                    sd_cmd_reg_dirty <= 1'b0;
                end
        end
            
        
        /*
          SD state machine
          
         */
    always @(posedge clk_in)
        begin
            case (sd_state)
                ST_RESET:
                    begin
                        sd_cs <= 1'b1;
                        sd_state <= ST_WAIT_FOR_CMD;
                    end
                   ST_WAIT_FOR_CMD:
                    begin
                        if (sd_cmd_reg_dirty && (!sd_cmd_acknowledged))
                            begin
                                if (sd_cmd_reg_0[CMD_RECV_BIT])
                                    begin
                                        sd_state <= ST_ACK_CMD_RECEIVE;
                                        sd_cmd_acknowledged <= 1'b1;
                                    end
                                else
                                    if (sd_cmd_reg_0[CMD_SEND_BIT])
                                        begin
                                            sd_state <= ST_ACK_CMD_TRANSMIT;
                                            sd_cmd_acknowledged <= 1'b1;
                                        end
                            end
                    end
                ST_ACK_CMD_RECEIVE: // receive
                    begin
                        sd_buff_addr <= 9'h0;
                        sd_inhibit_sdo <= 1'b1;
                        sd_inhibit_write <= 1'b0;
                        
                        sd_buffer <= sd_cmd_reg_2[CMD_BUFF_BIT]; // buffer selection
                        if (sd_cmd_reg_2[CMD_FULL_PAGE]) // full page ?
                            sd_tfr_size <= 9'h1ff;
                        else
                            sd_tfr_size <= { 3'h0, sd_cmd_reg_2[5:0] };
                        sd_state <= ST_TRANSFER;
                        sd_cs <= sd_cmd_reg_0[CMD_CS_BEG_BIT];
                    end
                ST_ACK_CMD_TRANSMIT:
                    begin
                        sd_buff_addr <= 9'h0;
                        sd_inhibit_write <= 1'b1;
                        sd_inhibit_sdo <= 1'b0;
                        sd_buffer <= sd_cmd_reg_2[CMD_BUFF_BIT]; // buffer selection
                        if (sd_cmd_reg_2[CMD_FULL_PAGE]) // full page ?
                            sd_tfr_size <= 9'h1ff;
                        else
                            sd_tfr_size <= { 3'h0, sd_cmd_reg_2[5:0] };
                        sd_state <= ST_TRANSFER;
                        sd_cs <= sd_cmd_reg_0[CMD_CS_BEG_BIT];
                    end
                ST_TRANSFER: // memory read start
                    begin
                        sd_state <= ST_TRANSFER_START;
                    end
                ST_TRANSFER_START:
                    begin
                        if (sd_cmd_reg_0[CMD_USE_CRC] && (sd_tfr_size == 'h0))
                            sd_shift_reg_o <= crc7[6:0] | 1'b1; // uses CRC7 as last command byte
                        else
                            sd_shift_reg_o <= sd_mem_o;
                        sd_bit_cnt <= 3'd7;
                        sd_state <= ST_SEND_BITS;
                        sd_mem_write <= 1'b0;
                    end
                ST_SEND_BITS:
                    begin
                        sd_shift_reg_o <= sd_shift_reg_o << 1;
                        sd_shift_reg_in <= (sd_shift_reg_in << 1) | sd_data_in; // data capture happens one clock after sd_clk
                        if (sd_bit_cnt)
                            begin
                                sd_bit_cnt <= sd_bit_cnt - 8'd1;
                            end
                        else
                            begin
                                sd_state <= ST_SEND_BITS_END;
                                if (sd_inhibit_write) // only for transmit
                                    sd_buff_addr <= sd_buff_addr + 9'd1;
                            end
                    end
                ST_SEND_BITS_END:
                    begin
                        if (sd_tfr_size)
                            begin
                                sd_tfr_size <= sd_tfr_size - 9'd1;
                                 sd_state <= ST_TRANSFER_START;
                            end
                        else
                            begin
                                sd_state <= ST_WAIT_FOR_CMD;
                                sd_cmd_acknowledged <= 1'b0;
                                sd_cs <= sd_cmd_reg_0[CMD_CS_END_BIT]; // to chain commands
                            end
                        if (!sd_inhibit_write)
                            begin
                                sd_mem_write <= 1'b1;
                                sd_buff_addr <= sd_buff_addr + 9'd1;
                            end
                    end
                    
            endcase      
    
        end
    
    initial
        begin
            sd_state = 0;
            sd_cmd_acknowledged = 0;
            sd_cmd_reg_dirty = 0;
            crc7 = 0;
        end
    
    endmodule
    
    `ifdef SIMULATOR
    // double port memory
    
    module ram_dp_1k(
        input wire          clka,
        input wire [9:0]    addra,
        input wire          writea,
        input wire          reada,
        input wire [7:0]    dataa,
        output reg [7:0]    qa,
        input wire          clkb,
        input wire [9:0]    addrb,
        input wire          writeb,
        input wire          readb,
        input wire [7:0]    datab,
        output reg [7:0]    qb
        );
        
    reg [7:0] mem[1023:0];
    
    
    always @(posedge clka)
        begin
            if (writea)
                mem[addra] <= dataa;
            qa <= mem[addra];
        end
            
    always @(posedge clkb)
        begin
            if (writeb)
                mem[addrb] <= datab;
            qb <= mem[addrb];
        end
            
    endmodule
    
    `endif
       
    

    A testbench is here:
    module tb_sd_card();
    
    reg sd_clk, cpu_clk;
    reg [10:0] cpu_addr;
    reg [7:0] cpu_data_o;
    reg cpu_we;
    reg cpu_oe;
    reg sd_data_to_driver;
    wire wled, rled;
    
    
    
    sd_driver drv(
        .clk_in(sd_clk),
        
        
        // sd interface
        
        .sd_clk(),
        .sd_cs_n(),
        .sd_data_o(),
        .sd_data_in(sd_data_to_driver),
        
        // cpu interface
        .cpu_clk_in(cpu_clk),     // memory read/write clock
        .cpu_addr_in(cpu_addr),
        .cpu_oe_in(cpu_oe),
        .cpu_we_in(cpu_we),
        .cpu_data_in(cpu_data_o),
        .cpu_data_o(),
        
        .wled_o(wled),         // activity led write
        .rled_o(rled)          // activity led read
        
        );
        
    always
        #25 sd_clk = ~ sd_clk; // 20 MHz clock
        
    always
        #16.67 cpu_clk = ~cpu_clk; // almost 33 MHz clock
    
        
    initial
        begin
            $dumpvars;
            sd_clk = 0;
            cpu_clk = 1;
            cpu_addr = 0;
            cpu_oe = 0;
            cpu_we = 0;
            sd_data_to_driver = 0;
            // ask status
            #100
            cpu_addr = 0;
            cpu_oe = 1;
            #49
            cpu_oe = 0;
            
            // memory write test
            #100
            cpu_addr = 'h400;
            cpu_data_o = 8'h40;
            cpu_we = 1;
            #49
            cpu_we = 0;
            
            #100
            cpu_addr = 'h401;
            cpu_data_o = 8'h00;
            cpu_we = 1;
            #49
            cpu_we = 0;
            
            #100
            cpu_addr = 'h402;
            cpu_data_o = 8'h00;
            cpu_we = 1;
            #49
            cpu_we = 0;
            #100
            cpu_addr = 'h403;
            cpu_data_o = 8'h00;
            cpu_we = 1;
            #49
            cpu_we = 0;
            #100
            cpu_addr = 'h404;
            cpu_data_o = 8'h00;
            cpu_we = 1;
            #49
            cpu_we = 0;
            #100
            // transfer size...
            cpu_data_o = 8'b0_0_000101; // 5 bytes from lower buffer to sd card
            cpu_addr = 'h002;
            cpu_we = 1;
            #49
            cpu_we = 0;
            #50
            // transfer CS should remain low after end of command
            cpu_data_o = 8'b0_1_0_0_1_000; // CS low all the time, use CRC as last byte
            cpu_addr = 'h000;
            cpu_we = 1;
            #49
            cpu_we = 0;
            
            // ask status
            #100
            cpu_addr = 0;
            cpu_oe = 1;
            #49
            cpu_oe = 0;
            #2000
            // ask status
            #100
            cpu_addr = 0;
            cpu_oe = 1;
            #49
            cpu_oe = 0;
            #100
            
            
            #2000
            $finish;
        end
        
    endmodule
    
    

    run it from a script like:
    @echo off
    set path=c:\iverilog\bin;%PATH%
    iverilog -I . -o sd.out -D SIMULATOR=1 tb_sd_driver.v sd_driver.v
    if errorlevel == 1 goto error
    vvp sd.out
    :error
    

    The path thing is needed in case some dll breaks iverilog, who knows which one ... I love dll hell :(
  • pik33pik33 Posts: 2,366
    edited 2014-10-07 11:24
    This is I wrote a year ago for my NIOS presentation system - it needs 2x SPI clock. SDHC only. It has read and write buffers for one sector. I plan to add it to my P1V system :)
    module sd1(clk,rst,sclk,miso,mosi,cs,st,addr,secnum,datain,dataout,stb,wr,rd,ready,err,debug);
    
    //Piotr Kardasz pik33@o2.pl
    //free code - gpl 2.0 or higher
    
    input wire clk;
    input wire rst;
    output reg sclk;
    output reg mosi;
    output reg cs;
    input wire miso;
    input wire [6:0] addr;
    input wire [31:0] secnum;
    input wire stb; // write strobe
    input wire wr; // start sector write
    input wire rd; // start sector read
    input wire [31:0] datain;
    output reg [31:0] dataout;
    output reg ready;
    output reg err;
    
    output reg [7:0] st;
    output reg[7:0] debug;
    
    reg [7:0] wordcnt;
    reg [31:0] writebuf[128];
    reg [31:0] readbuf[128];
    
    reg[15:0] cnt;
    reg[4:0] state;
    reg slowspiclk;
    reg fastspiclk;
    reg oldrst;
    reg oldstb;
    reg oldwr;
    reg oldrd;
    reg[56:0] cmd;
    reg[48:0] res;
    reg[48:0] res2;
    reg[26:0] cnt2;
    reg clk2;
    reg [23:0] watchdog;
    reg[31:0]qqqq;
    
    initial
      begin
      cnt<=0;
      state<=0;
      mosi<=1;
      sclk<=1;
      cs<=1;
      res<=0;
      ready<=0;
      err<=0;
      watchdog<=0;
    //  $readmemh("wb.hex",writebuf);
      end
      
    always
      begin
      st<={err,ready,1'b0,state};
      if (state==0) sclk<=1;
        else if ((state>=1) && (state<14)) sclk<=slowspiclk;
        else sclk<=fastspiclk;
      end
      
    always@(posedge clk)
        qqqq<=writebuf[wordcnt];
    
    always@(posedge clk)
      begin
      slowspiclk<=cnt[7]; //195 kHz
      fastspiclk<=cnt[0]; //25 MHz 
      dataout<=readbuf[addr];
      oldrst<=rst;
      oldstb<=stb;
      
      if ((stb==1) && (oldstb==0)) writebuf[addr]<=datain;
      
      if ((rst==1) && (oldrst==0)) //posedge reset
        begin
         cnt<=0;
         err<=0;
         state<=1;
         mosi=1;
         cs=1;
         end
    
      else // if not reset 
        begin    
      //generate err and ready
     
        if (state==14)
          begin  
             err<=0;
            ready<=1;
           watchdog<=0;
           end
        else
          begin
            ready<=0;
          if (watchdog>10_000_000) 
              begin
              err<=1;
              watchdog<=10_000_001;
              end
            else
              watchdog<=watchdog+1;
           end
        
      // init state machine
        if (state==0) //idle after powerup
          begin
           cnt<=0;
           mosi<=1;
           cs<=1;
           end
         
        else if (state==1) //send 88 clocks
          begin
           if (cnt[15:8]==88) 
             begin
              cmd<=56'b11111111_01_000000_00000000_00000000_00000000_00000000_10010101; //cmd0
              cnt<=1;
              state<=2;
              cs=0;
            end
          else cnt<=cnt+1;
          end
      
        else if (state==2) // send cmd0
          begin
          if (cnt[7:0]==4) mosi<=cmd[55];
          if (cnt[7:0]==8)
             begin
             cmd<=(cmd<<1) | 56'b1;    
            end 
          if (cnt[15:8]==56)
            begin
            state<=3;
            cnt<=1;
            mosi<=1;
              end
          else
            cnt<=cnt+1;        
           end    
        
        else if (state==3) // wait for 0 on miso    
          begin
           if ((cnt[7:0]==127) && (miso==0))
             begin
              state<=4;
              cnt<=128;
            res<=(res<<1) | miso;
              end
           else cnt<=cnt+1; 
            end
         
        else if (state==4)
          begin
          if (cnt[7:0]==127) res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[15:8]==8) // TODO! check if res=1
             begin
                state<=5;
              cnt<=0;
              cmd<=56'b11111111_01_001000_00000000_00000000_00000001_10101010_10000111; //cmd8
              end 
           end
         
        else if (state==5) // send cmd8
          begin
          if (cnt[7:0]==4)
             mosi<=cmd[55];
          if (cnt[7:0]==8)
             begin
             cmd<=(cmd<<1) | 56'b1;    
            end 
          if (cnt[15:8]==56)
            begin
            state<=6;
            cnt<=1;
            end
          else
            cnt<=cnt+1;        
           end    
         
        else if (state==6) // wait for 0 on miso    
          begin
           if ((cnt[7:0]==127) &&(miso==0))
             begin
              state<=7;
              cnt<=128;
              end
           else cnt<=cnt+1;    
          end
    
        else if (state==7)
          begin
          if (cnt[7:0]==127)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[15:8]==40)
             begin
              state<=8;
              cnt<=0;
              cmd<=56'hFF_77_00_00_00_00_FF;
             end
          end
    
        else if (state==8) // send cmd55
          begin
          if (cnt[7:0]==4)
             mosi<=cmd[55];
          if (cnt[7:0]==8)
             begin
             cmd<=(cmd<<1) | 56'b1;    
            end 
          if (cnt[15:8]==56)
            begin
            state<=9;
            cnt<=0;
            end
          else
            cnt<=cnt+1;
            end         
         
        else if (state==9) // wait for 0 on miso    
          begin
           if ((cnt[7:0]==127) &&(miso==0))
             begin
              state<=10;
              cnt<=128;
              end
           else cnt<=cnt+1;    
          end     
         
        else if (state==10)
          begin
          if (cnt[7:0]==127)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[15:8]==8) // TODO! check if res=1
             begin
              state<=11;
              cnt<=1;
              cmd<=56'hFF_69_40_00_00_00_FF; //cmd41
            end 
           end
              
        else if (state==11) // send cmd41
          begin
          if (cnt[7:0]==4)
           mosi<=cmd[55];
          if (cnt[7:0]==8)
             begin
             cmd<=(cmd<<1) | 56'b1;    
            end 
          if (cnt[15:8]==56)
            begin
            state<=12;
            cnt<=1;
            end
          else
            cnt<=cnt+1;
           end             
     
        else if (state==12) // wait for 0 on miso    
          begin
              if ((cnt[7:0]==127) &&(miso==0))
             begin
              state<=13;
              cnt<=128;
              end
           else cnt<=cnt+1;    
           end          
         
        else if (state==13)
          begin
          if (cnt[7:0]==127)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[15:8]==8) 
             begin
              if (res[7:0]==0)
               begin
                state<=14;
                cnt<=0;
                cs<=1;
                end
              else
              begin        
                cmd<=56'hFF_77_00_00_00_00_FF; //cmd55
                state<=8;
                cnt<=1;
                end
            end 
           end               
        
        else if (state==14) // ready for read/write
          begin
           if ((rd==1) && (oldrd==0)) // posedge read
            begin
              cmd<= {16'hFF_51,secnum,8'hFF};// (56'hFF_51_00_00_00_00_FF | (secnum<<8));
              state<=15;
              cnt<=0;
              cs<=0;
            end
          else if ((wr==1) && (oldwr==0)) // posedge write
             begin
              cmd<={16'hFF_58,secnum,8'hFF};//(56'hFF_58_00_00_00_00_FF | (secnum<<8));
              state<=16;
              cnt<=1;
              cs<=0;
              end
           else
             begin 
             state<=14;
             cnt<=0;
             cs<=1;
             end    
           end    
         
        else if (state==15) // send cmd17     
          begin
          if (cnt[0]==0)
            begin 
            mosi<=cmd[55];
             cmd<=(cmd<<1) | 56'b1;    
            end
          if (cnt[7:1]==56)
            begin
            state<=17;
            cnt<=1;
            end
          else
             begin
              state<=15;
            cnt<=cnt+1;
              end
           end    
        
        else if (state==17) // wait for 0 on miso    
          begin
           if (cnt[0]==1)
            begin 
            mosi<=cmd[55];
             res<=(cmd<<1) | miso;
            end
           if ((cnt[0]==1) && (miso==0))
             begin
              state<=18;
              cnt<=0;
              end
           else 
             begin
              cnt<=cnt+1;   
            state<=17;
            end
           end         
    
        else if (state==18) //get response
          begin
          if (cnt[0]==1) res<=(res<<1) | miso;
           if (cnt[7:1]==8) // TODO! check if res=1
             begin
             debug<=res;
              state<=19;
              cnt<=1;
            end 
           else
             begin
                cnt<=cnt+1; 
              state<=18;
              end;
           end
    
        else if (state==19) // wait for 0 on miso    => FE sent
          begin
           if (cnt[0]==1) res<=(res<<1) |miso;
           if ((cnt[0]==1) && (miso==0))
             begin
             debug<=res;
              state<=20;
              cnt<=0;
             wordcnt<=0; 
             end
           else 
             begin
             cnt<=cnt+1;   
            state<=19;
              end
           end     
         
        else if (state==20)  
          begin
          if (cnt[0]==1) res<=(res<<1) | miso;
          if (cnt[7:1]==32) 
             begin
                cnt<=1;
            wordcnt<=wordcnt+1;
              if (wordcnt==129)
                begin
                state<=14;
                cnt<=0;
                cs<=1;
                end
              else if (wordcnt<128)
                  begin
                readbuf[wordcnt]<=res;
                end
            end 
          else
             begin 
             cnt<=cnt+1;
            state<=20;
            end        
          end     
         
    /// ------ write -----     
         
        else if (state==16) // send cmd24     
          begin
          if (cnt[0]==0)
            begin 
            mosi<=cmd[55];
             cmd<=(cmd<<1) | 56'b1;    
            end
          if (cnt[7:1]==56)
            begin
            state<=21;
            cnt<=0;
            end
          else
            cnt<=cnt+1;
           end    
        
        else if (state==21) // wait for 0 on miso    
          begin
           if (miso==0)
             begin
              state<=22;
              cnt<=0;
              end
           else cnt<=cnt+1;   
           end         
    
        else if (state==22) //get response
          begin
          if (cnt[0]==1)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[7:1]==8) // TODO! check if res=1
             begin
              state<=23;
              cnt<=0;
              cmd<=(48'hFF_FE_00_00_00_00 | (secnum<<8));
            end 
           end
    
        else if (state==23 ) //send ---- FFFE----!!!----
          begin
          res<=32'hFF_FF_FF_FE;
           state<=24;
           wordcnt=0;
           end    
     
        else if (state==24) // send writebuf
          begin
          if (cnt[0]==0)
            begin 
            mosi<=res[31];
             res<=(res<<1) | 32'b1;    
            end
          if (cnt[7:1]==32)
             begin
              cnt<=0;
             wordcnt<=wordcnt+1;
             if (wordcnt<128) res<=qqqq; 
              if (wordcnt==128)
                begin
                state<=25;
                res<=32'hffffffff;
                end
             end
           else cnt<=cnt+1;    
          end
         
        else if (state==25)// send ffff  
          begin
          if (cnt[0]==1) res<=(res<<1) | 32'b1;
           if (cnt[7:1]==16) 
             begin
              cnt<=0;
            state<=26;
              end
          else cnt<=cnt+1;        
           end    
    
        else if (state==26) // wait for 0 on miso    
          begin
             if ((cnt[0]==1) && (miso==0))
             begin
              state<=27;
              cnt<=0;
              end
           else cnt<=cnt+1;   
          end
         
        else if (state==27) //get response
          begin
          if (cnt[0]==1)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[7:1]==8) 
             begin
              debug<=res;
              if (res[7:4]==4'b0101) 
                  begin
                state<=28;
                cnt<=0;
                end
            end 
           end
         
        else if (state==28) //wait until ff
          begin
          if (cnt[0]==1)
            res<=(res<<1) | miso;
           cnt<=cnt+1;
           if (cnt[7:1]==8) 
             begin
    //        debug<=res;
                if (res[7:0]==8'hFF) 
                  begin
                cs<=1;
                state<=14;
                cnt<=0;
                end
            end 
           end
            
        else // error!!!
          begin
          cnt<=0;
          mosi<=1;
          cs<=1;
          state<=31;
          end     
        end 
      end
    endmodule  
    
  • AleAle Posts: 2,363
    edited 2014-10-07 12:30
    That's of course much more than what I wrote. I was planning on a mixed driver, with a bit of help from a uC and not a whole hard driver so to say. How much resources does it need ? Mine needs 66 slices and does nothing :(...
  • jac_goudsmitjac_goudsmit Posts: 418
    edited 2014-10-07 12:34
    I fail to see how the previous 3 posts fit in this thread...

    ===Jac
  • pik33pik33 Posts: 2,366
    edited 2014-10-07 12:40
    I fail to see how the previous 3 posts fit in this thread...

    ===Jac

    We want a new retrocomputer :)
  • pik33pik33 Posts: 2,366
    edited 2014-10-07 12:42
    Ale wrote: »
    That's of course much more than what I wrote. I was planning on a mixed driver, with a bit of help from a uC and not a whole hard driver so to say. How much resources does it need ? Mine needs 66 slices and does nothing :(...

    About resources, I'll check it tomorrow. Maybe my code can help to make your driver run.
  • AleAle Posts: 2,363
    edited 2014-10-07 22:36
    @jac: That was a bit rude, and undeserved: pik33 spoke about integrating a sd driver and I decided to share part of my work... I shouldn't have to explain myself..

    @pki: It actually works,
  • 65816581 Posts: 132
    edited 2014-10-08 06:06
    This all looks very promising. @pik: How to use your HDL SD Card driver? I didn't look into it yet.
    If it would be possible to attach this to the 6502 address/data bus (without propeller) and just read and write
    to it using the OSI BASICs PEEK and POKE - i think that would be ideal to start with. I have to see how I implement
    something like SYS on the OSI BASIC. Work in progess https://git.no-route.org/syso/bemicro-cv-multicomp-6502/tree/master/ROMS/6502
    (you might also want to look into subdirectories, pik33)
  • AleAle Posts: 2,363
    edited 2014-10-08 07:20
    @6581: It has a 32 bit interface, sort of. A wrapper or similar may be needed. One could also define a shared memory block (like i did) with two different port widths to allow for mixed access.
  • pik33pik33 Posts: 2,366
    edited 2014-10-08 07:36
    To use this with 6502 it needs to change buffers structure from 128x32 to 512x8, so 6502 can read and write the buffers without any trouble. The sector number is also 32 bit, so there is some work needed to make it from 4 8-bit 6502 transfers. Maybe there will be better to add some hardware translation between the 6502 and the driver. Of course if we have a 6502 and the Propeller in one system, all these things can be done by the prop and then used by 6502.

    This is a driver which can do 3 things: initialize the card, read a sector, write a sector.
    To read a sector you have to set the sector number, strobe the read and wait for ready, then get 128 longs from the buffer.
    To write a sector you have to fill the write buffer, then strobe the write and wait for ready.

    I used this driver with Nios II/e, accessing it with some parallel ports. Then I found open source fat32 driver written in c. It needed only attaching 3 functions: sector read, sector write and initialize. So, I wrote these 3 functions and it worked with fat32 formatted sdhc. I hope the driver can be compiled with prop gcc in xmm mode and then executed from de2-115 sram. Or maybe sdram, if I managed to attach it to my system.

    My retrocomputer vga thing is now near the 1.0 state. Today I managed to add the sprite machine to it. So I have all the retrocomputer should have: a lot of different graphisc modes, from 1792x1120x4 colors to 448x280x256 colors, horizontal scroll register, display list, 4 pallette banks and some (now 4, but adding more is simply ctrl-c,ctrl-v thing) 32x32 sprites with scalable x and y zoom. This is all the Atari 8-bit and C64 have, except the resolutions are more recent.

    I think it may be possible to add a 6502 with 64 k ram made from fpga ram blocks. Then this ram can be accessed by p1v, so if the 6502 makes some work, for example, drive a SID/POKEY/AY chip, the Propeller can fetch this and in this example, make a sound using SIDCog/AYcog/POKEY emulator.
  • jac_goudsmitjac_goudsmit Posts: 418
    edited 2014-10-08 10:59
    Ale wrote: »
    @jac: That was a bit rude, and undeserved: pik33 spoke about integrating a sd driver and I decided to share part of my work... I shouldn't have to explain myself..

    I apologize if my remark was interpreted that way; I didn't intend to be rude.

    Using the on-board SD card reader on the BeMicro CV and other boards would be extremely useful and would be something I'm interested in, too (especially because there's no useful EEPROM). But accessing the SD card and booting from it is a feature that's not going to be specific to my Propeddle project. It would have been better to start a new thread about it, rather than add it as follow-up here, so that others (who may not be interested in the Propeddle project) might see it. So I was basically wondering why it was posted here.

    Incidentally, I am considering adding an SD card reader for mass storage to the Propeddle project, but it will be connected to some Propeller pins so it can use a driver from OBEX to access it (no Verilog or FPGA Propeller needed); however, unless there's such a thing as an I2C-based SD card reader, it'll have to share pins with the 6502 so it will require a chip to enable or disable it while the system is running normally. You heard it here first :-)

    I'm seeing some great ideas, and I wish I had more time to look into it. Never mind me folks, carry on! :-)

    ===Jac
  • AleAle Posts: 2,363
    edited 2014-10-08 11:25
    The SD socket can be used by the P1V, 4 pins have to be remapped for that, lets say you want P0..P3 instead of assigning them to the 2x20 header, you choose the pins from the SD socket :). And take any OBEX driver :). But that is a sort of "software" solution. A hw driver could help a big and maybe do not clog a cog :)
  • pik33pik33 Posts: 2,366
    edited 2014-10-08 11:35
    Ale wrote: »
    The SD socket can be used by the P1V, 4 pins have to be remapped for that, lets say you want P0..P3 instead of assigning them to the 2x20 header, you choose the pins from the SD socket :). And take any OBEX driver :). But that is a sort of "software" solution. A hw driver could help a big and maybe do not clog a cog :)

    This of course works, I published an audio player here, but it is not as fast as a hardware driver can be. Modern SD can use 50 MHz SPI clock whiile the Propeller can read @ 5 Mbps.
  • David BetzDavid Betz Posts: 14,516
    edited 2014-10-08 17:01
    pik33 wrote: »
    This of course works, I published an audio player here, but it is not as fast as a hardware driver can be. Modern SD can use 50 MHz SPI clock whiile the Propeller can read @ 5 Mbps.
    A hardware driver would be interesting but is it likely that that sort of thing would ever make it into P1+ silicon? I guess it is interesting as an academic exercise or if you think you can actually deploy a P1v. I guess that would be possible for a product that is not price sensitive. Is that what you're thinking?
  • pik33pik33 Posts: 2,366
    edited 2014-10-08 22:58
    The P1+ silicon may contain not only this type of sd driver, which is simply spi+state machine, but the real 4-bit SD interface with hardware CRC. The USB SD reader which can read 20 MB/s costs here 4 PLN which is something like 1.25 $ - the p1+ chip should have such thing on board.
    And something else: the coprocessor. There are some operation which are LE expensive, for example division. The div/mod thing needs 2500 LEs. So let there be some addresses where the propeller can put the data and then get the result. I will add this to my sram/vga driver.
Sign In or Register to comment.