OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [sim/] [SPI.cpp] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 jamieiles
// Copyright Jamie Iles, 2017
2
//
3
// This file is part of s80x86.
4
//
5
// s80x86 is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// s80x86 is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
#include "SPI.h"
19
#include "CPU.h"
20
 
21
#include <iostream>
22
#include <iomanip>
23
#include <stdint.h>
24
 
25
SPI::SPI(const std::string &disk_image_path)
26
    : IOPorts(0xfff0, 2),
27
      control_reg(0),
28
      rx_val(0),
29
      state(STATE_IDLE),
30
      disk_image(disk_image_path,
31
                 std::ios::binary | std::ios::in | std::ios::out),
32
      do_write(false)
33
{
34
}
35
 
36
void SPI::write8(uint16_t port_num, unsigned offs, uint8_t v)
37
{
38
    auto shift = 8 * offs;
39
    uint16_t mask = 0xff << shift;
40
 
41
    if (port_num == 0) {
42
        control_reg &= ~mask;
43
        control_reg |= static_cast<uint16_t>(v) << shift;
44
    } else {
45
        if (offs == 0)
46
            transfer(v);
47
    }
48
}
49
 
50
void SPI::write16(uint16_t port_num, uint16_t v)
51
{
52
    write8(port_num, 0, v);
53
    write8(port_num, 1, v >> 8);
54
}
55
 
56
uint8_t SPI::read8(uint16_t port_num, unsigned offs)
57
{
58
    auto shift = 8 * offs;
59
 
60
    if (port_num == 0)
61
        return control_reg >> shift;
62
    else
63
        return offs == 0 ? rx_val : 0x00;
64
}
65
 
66
uint16_t SPI::read16(uint16_t __unused port_num)
67
{
68
    return read8(port_num, 0) |
69
           (static_cast<uint16_t>(read8(port_num, 1)) << 8);
70
}
71
 
72
void SPI::transfer(uint8_t mosi_val)
73
{
74
    switch (state) {
75
    case STATE_IDLE:
76
        do_write = false;
77
        if (mosi_val != 0xff && !(control_reg & (1 << 9))) {
78
            state = STATE_RECEIVING;
79
            mosi_buf.push_back(mosi_val);
80
        }
81
        rx_val = 0xff;
82
        break;
83
    case STATE_RECEIVING:
84
        mosi_buf.push_back(mosi_val);
85
        if (transmit_ready())
86
            state = STATE_TRANSMITTING;
87
        rx_val = 0xff;
88
        break;
89
    case STATE_TRANSMITTING:
90
        if (miso_buf.size() == 0) {
91
            rx_val = 0xff;
92
            state = do_write ? STATE_WAIT_FOR_DATA : STATE_IDLE;
93
            mosi_buf.clear();
94
        } else {
95
            rx_val = miso_buf[0];
96
            miso_buf.pop_front();
97
        }
98
        break;
99
    case STATE_WAIT_FOR_DATA:
100
        rx_val = 0xff;
101
        if (mosi_val == 0xfe)
102
            state = STATE_DO_WRITE_BLOCK;
103
        break;
104
    case STATE_DO_WRITE_BLOCK:
105
        if (write_count < 512) {
106
            disk_image.put(mosi_val);
107
            rx_val = 0xff;
108
        } else if (write_count < 514) {
109
            // CRC
110
            rx_val = 0xff;
111
        } else {
112
            rx_val = 0x5; // Received OK.
113
            state = STATE_IDLE;
114
        }
115
        ++write_count;
116
        break;
117
    };
118
}
119
 
120
bool SPI::transmit_ready()
121
{
122
    assert(mosi_buf.size() > 0);
123
 
124
    switch (mosi_buf[0]) {
125
    case 0x77: // ACMD
126
    case 0x40: // Reset, fallthrough
127
    case 0x48: // IF COND
128
    case 0x50: // Set blocklen
129
        if (mosi_buf.size() >= 7) {
130
            miso_buf = {0x01};
131
            return true;
132
        }
133
        break;
134
    case 0x69: // CMD41, reset
135
        if (mosi_buf.size() >= 7) {
136
            miso_buf = {0x00};
137
            return true;
138
        }
139
        break;
140
    case 0x51: // Read block
141
        if (mosi_buf.size() >= 7) {
142
            read_block();
143
            return true;
144
        }
145
        break;
146
    case 0x58: // Write block
147
        if (mosi_buf.size() >= 7) {
148
            write_block();
149
            return true;
150
        }
151
        break;
152
    case 0x7a: // OCR
153
        if (mosi_buf.size() >= 7) {
154
            miso_buf = {0x01, 0x00, 0x00, 0x00, 0x00};
155
            return true;
156
        }
157
        break;
158
    default:
159
        std::cout << "unknown command " << std::hex << (unsigned)mosi_buf[0]
160
                  << std::endl;
161
        // Generic error
162
        miso_buf = {0xfe};
163
        return true;
164
    }
165
 
166
    return false;
167
}
168
 
169
void SPI::read_block()
170
{
171
    union {
172
        uint8_t u8[4];
173
        uint32_t u32;
174
    } converter = {
175
        .u8 = {mosi_buf[4], mosi_buf[3], mosi_buf[2], mosi_buf[1]},
176
    };
177
    // Padding, R1, padding, start of data
178
    miso_buf = {0xff, 0xff, 0x00, 0xff, 0xfe};
179
    // Data
180
    disk_image.seekg(converter.u32, std::ios::beg);
181
    for (auto m = 0; m < 512; ++m)
182
        miso_buf.push_back(disk_image.get());
183
    // CRC
184
    for (auto m = 0; m < 2; ++m)
185
        miso_buf.push_back(0x77);
186
}
187
 
188
void SPI::write_block()
189
{
190
    union {
191
        uint8_t u8[4];
192
        uint32_t u32;
193
    } converter = {
194
        .u8 = {mosi_buf[4], mosi_buf[3], mosi_buf[2], mosi_buf[1]},
195
    };
196
 
197
    // Padding, R1, padding, Accepted
198
    miso_buf = {
199
        0xff, 0x00, 0xff, 0xff, 0x05,
200
    };
201
    do_write = true;
202
    write_count = 0;
203
    disk_image.seekg(converter.u32, std::ios::beg);
204
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.