nicbm.h 6.02 KB
Newer Older
Antoine Kaufmann's avatar
Antoine Kaufmann committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Copyright 2021 Max Planck Institute for Software Systems, and
 * National University of Singapore
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

25
#include <set>
26
#include <deque>
27
28
29
extern "C" {
    #include <nicsim.h>
}
30

31
32
33
34
35
36
37
namespace nicbm {

#include <cassert>


static const size_t MAX_DMA_LEN = 2048;

Antoine Kaufmann's avatar
Antoine Kaufmann committed
38
39
40
41
42
43
44
class DMAOp {
    public:
        virtual ~DMAOp() { }
        bool write;
        uint64_t dma_addr;
        size_t len;
        void *data;
45
46
};

47
48
49
50
51
52
53
class TimedEvent {
    public:
        virtual ~TimedEvent() { }
        uint64_t time;
};


Antoine Kaufmann's avatar
Antoine Kaufmann committed
54
55
56
57
58
59
/**
 * The Runner drives the main simulation loop. It's initialized with a reference
 * to a device it should manage, and then once `runMain` is called, it will
 * start interacting with the PCI and Ethernet queue and forwarding calls to the
 * device as needed.
 * */
60
61
62
class Runner {
    public:
        class Device {
63
64
65
66
67
            protected:
                bool int_intx_en;
                bool int_msi_en;
                bool int_msix_en;

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
            public:
                /**
                 * Initialize device specific parameters (pci dev/vendor id,
                 * BARs etc. in intro struct.
                 */
                virtual void setup_intro(struct cosim_pcie_proto_dev_intro &di)
                    = 0;

                /**
                 * execute a register read from `bar`:`addr` of length `len`.
                 * Should store result in `dest`.
                 */
                virtual void reg_read(uint8_t bar, uint64_t addr, void *dest,
                        size_t len) = 0;

                /**
                 * execute a register write to `bar`:`addr` of length `len`,
                 * with the data in `src`.
                 */
                virtual void reg_write(uint8_t bar, uint64_t addr,
                        const void *src, size_t len) = 0;

                /**
                 * the previously issued DMA operation `op` completed.
                 */
                virtual void dma_complete(DMAOp &op) = 0;

                /**
                 * A packet has arrived on the wire, of length `len` with
                 * payload `data`.
                 */
                virtual void eth_rx(uint8_t port, const void *data, size_t len)
                    = 0;
101
102
103
104
105

                /**
                 * A timed event is due.
                 */
                virtual void timed_event(TimedEvent &ev);
106
107
108
109
110
111

                /**
                 * Device control update
                 */
                virtual void devctrl_update(
                        struct cosim_pcie_proto_h2d_devctrl &devctrl);
112
113
114
        };

    protected:
115
116
117
118
119
120
121
        struct event_cmp {
            bool operator() (TimedEvent *a, TimedEvent *b)
            {
                return a->time < b->time;
            }
        };

122
        Device &dev;
123
        std::set<TimedEvent *, event_cmp> events;
124
125
        std::deque<DMAOp *> dma_queue;
        size_t dma_pending;
126
        uint64_t mac_addr;
127
        struct nicsim_params nsparams;
128
        struct cosim_pcie_proto_dev_intro dintro;
129
130
131
132
133
134
135
136

        volatile union cosim_pcie_proto_d2h *d2h_alloc(void);
        volatile union cosim_eth_proto_d2n *d2n_alloc(void);

        void h2d_read(volatile struct cosim_pcie_proto_h2d_read *read);
        void h2d_write(volatile struct cosim_pcie_proto_h2d_write *write);
        void h2d_readcomp(volatile struct cosim_pcie_proto_h2d_readcomp *rc);
        void h2d_writecomp(volatile struct cosim_pcie_proto_h2d_writecomp *wc);
137
        void h2d_devctrl(volatile struct cosim_pcie_proto_h2d_devctrl *dc);
138
139
140
141
142
        void poll_h2d();

        void eth_recv(volatile struct cosim_eth_proto_n2d_recv *recv);
        void poll_n2d();

143
144
        bool event_next(uint64_t &retval);
        void event_trigger();
145
146
147

        void dma_do(DMAOp &op);
        void dma_trigger();
148
149
150
    public:
        Runner(Device &dev_);

Antoine Kaufmann's avatar
Antoine Kaufmann committed
151
        /** Run the simulation */
152
153
154
155
156
        int runMain(int argc, char *argv[]);

        /* these three are for `Runner::Device`. */
        void issue_dma(DMAOp &op);
        void msi_issue(uint8_t vec);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
157
        void msix_issue(uint8_t vec);
158
        void eth_send(const void *data, size_t len);
159

160
161
162
        void event_schedule(TimedEvent &evt);
        void event_cancel(TimedEvent &evt);

163
        uint64_t time_ps() const;
164
        uint64_t get_mac_addr() const;
165
166
};

Antoine Kaufmann's avatar
Antoine Kaufmann committed
167
168
169
/**
 * Very simple device that just has one register size.
 */
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
template <class TReg = uint32_t>
class SimpleDevice : public Runner::Device {
    public:
        virtual TReg reg_read(uint8_t bar, uint64_t addr) = 0;
        virtual void reg_write(uint8_t bar, uint64_t addr, TReg val) = 0;

        virtual void reg_read(uint8_t bar, uint64_t addr, void *dest,
                size_t len)
        {
            assert(len == sizeof(TReg));
            TReg r = reg_read(bar, addr);
            memcpy(dest, &r, sizeof(r));
        }

        virtual void reg_write(uint8_t bar, uint64_t addr,
                const void *src, size_t len)
        {
            assert(len == sizeof(TReg));
            TReg r;
            memcpy(&r, src, sizeof(r));
            reg_write(bar, addr, r);
        }
};
}