Skip to main content

all_smi_luwen_ref/
lib.rs

1// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{
5    collections::{hash_map::Entry, HashMap},
6    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
7};
8
9use error::LuwenError;
10use all_smi_luwen_core::Arch;
11use all_smi_luwen_if::{FnDriver, FnOptions};
12use all_smi_ttkmd_if::{PciError, PossibleTlbAllocation};
13
14mod detect;
15pub mod error;
16mod wormhole;
17
18use wormhole::ethernet::{self, EthCommCoord};
19
20pub use detect::{
21    detect_chips, detect_chips_fallible, detect_chips_silent, detect_local_chips, start_detect,
22};
23pub use all_smi_ttkmd_if::{DmaBuffer, DmaConfig, PciDevice, Tlb};
24
25#[derive(Clone)]
26pub struct ExtendedPciDeviceWrapper {
27    inner: Arc<RwLock<ExtendedPciDevice>>,
28}
29
30impl ExtendedPciDeviceWrapper {
31    pub fn borrow_mut(&self) -> RwLockWriteGuard<ExtendedPciDevice> {
32        self.inner.as_ref().write().unwrap()
33    }
34
35    pub fn borrow(&self) -> RwLockReadGuard<ExtendedPciDevice> {
36        self.inner.as_ref().read().unwrap()
37    }
38}
39
40pub struct ExtendedPciDevice {
41    pub device: PciDevice,
42
43    pub harvested_rows: u32,
44    pub grid_size_x: u8,
45    pub grid_size_y: u8,
46
47    pub eth_x: u8,
48    pub eth_y: u8,
49    pub command_q_addr: u32,
50    pub fake_block: bool,
51
52    pub default_tlb: PossibleTlbAllocation,
53
54    pub ethernet_dma_buffer: HashMap<(u8, u8), DmaBuffer>,
55}
56
57impl ExtendedPciDevice {
58    pub fn open(pci_interface: usize) -> Result<ExtendedPciDeviceWrapper, all_smi_ttkmd_if::PciError> {
59        let device = PciDevice::open(pci_interface)?;
60
61        let (grid_size_x, grid_size_y) = match device.arch {
62            all_smi_luwen_core::Arch::Grayskull => (13, 12),
63            all_smi_luwen_core::Arch::Wormhole => (10, 12),
64            all_smi_luwen_core::Arch::Blackhole => (17, 12),
65        };
66
67        let default_tlb;
68
69        // Driver API 2+ has TLB allocation APIs supporting WH & BH.
70        if device.arch != Arch::Grayskull && device.driver_version >= 2 {
71            let size = match device.arch {
72                Arch::Wormhole => 1 << 24,  // 16 MiB
73                Arch::Blackhole => 1 << 21, // 2 MiB
74                _ => {
75                    return Err(PciError::TlbAllocationError(
76                        "Unsupported architecture for TLB allocation".to_string(),
77                    ))
78                }
79            };
80
81            if let Ok(tlb) = device.allocate_tlb(size) {
82                default_tlb = PossibleTlbAllocation::Allocation(tlb);
83            } else {
84                // Couldn't get a tlb... ideally at this point we would fallback to using a slower but useable read/write API
85                // for now though, we will just fail
86                return Err(PciError::TlbAllocationError(
87                    "Failed to find a free tlb".to_string(),
88                ));
89            }
90        } else {
91            // Otherwise fallback to default behaviour of just taking a constant one
92            default_tlb = PossibleTlbAllocation::Hardcoded(match device.arch {
93                all_smi_luwen_core::Arch::Grayskull | all_smi_luwen_core::Arch::Wormhole => 184,
94                all_smi_luwen_core::Arch::Blackhole => 190,
95            });
96        }
97
98        Ok(ExtendedPciDeviceWrapper {
99            inner: Arc::new(RwLock::new(ExtendedPciDevice {
100                harvested_rows: 0,
101                grid_size_x,
102                grid_size_y,
103                eth_x: 4,
104                eth_y: 6,
105                command_q_addr: 0,
106                fake_block: false,
107
108                default_tlb,
109
110                device,
111
112                ethernet_dma_buffer: HashMap::with_capacity(16),
113            })),
114        })
115    }
116
117    pub fn read_block(&mut self, addr: u32, data: &mut [u8]) -> Result<(), PciError> {
118        self.device.read_block(addr, data)
119    }
120
121    pub fn write_block(&mut self, addr: u32, data: &[u8]) -> Result<(), PciError> {
122        self.device.write_block(addr, data)
123    }
124}
125
126pub fn comms_callback(
127    ud: &ExtendedPciDeviceWrapper,
128    op: FnOptions,
129) -> Result<(), Box<dyn std::error::Error>> {
130    Ok(comms_callback_inner(ud, op)?)
131}
132
133pub fn comms_callback_inner(
134    ud: &ExtendedPciDeviceWrapper,
135    op: FnOptions,
136) -> Result<(), LuwenError> {
137    match op {
138        FnOptions::Driver(op) => match op {
139            FnDriver::DeviceInfo(info) => {
140                let borrow = ud.borrow();
141                if !info.is_null() {
142                    unsafe {
143                        *info = Some(all_smi_luwen_if::DeviceInfo {
144                            bus: borrow.device.physical.pci_bus,
145                            slot: borrow.device.physical.slot,
146                            function: borrow.device.physical.pci_function,
147                            domain: borrow.device.physical.pci_domain,
148
149                            interface_id: borrow.device.id as u32,
150
151                            vendor: borrow.device.physical.vendor_id,
152                            device_id: borrow.device.physical.device_id,
153                            board_id: borrow.device.physical.subsystem_id,
154                            bar_size: borrow.device.pci_bar.as_ref().map(|v| v.bar_size_bytes),
155                        });
156                    }
157                }
158            }
159        },
160        FnOptions::Axi(op) => match op {
161            all_smi_luwen_if::FnAxi::Read { addr, data, len } => {
162                if len > 0 {
163                    if len <= 4 {
164                        let output = ud.borrow_mut().device.read32(addr)?;
165                        let output = output.to_le_bytes();
166                        unsafe {
167                            data.copy_from_nonoverlapping(output.as_ptr(), len as usize);
168                        }
169                    } else {
170                        unsafe {
171                            ud.borrow_mut().read_block(
172                                addr,
173                                std::slice::from_raw_parts_mut(data, len as usize),
174                            )?
175                        };
176                    }
177                }
178            }
179            all_smi_luwen_if::FnAxi::Write { addr, data, len } => {
180                if len > 0 {
181                    // Assuming here that u32 is our fundamental unit of transfer
182                    if len <= 4 {
183                        let to_write = if len == 4 {
184                            let slice = unsafe { std::slice::from_raw_parts(data, len as usize) };
185                            u32::from_le_bytes(slice.try_into().unwrap())
186                        } else {
187                            // We are reading less than a u32, so we need to read the existing value first
188                            // then writeback the new value with the lower len bytes replaced
189                            let value = ud.borrow_mut().device.read32(addr)?;
190                            let mut value = value.to_le_bytes();
191                            unsafe {
192                                value
193                                    .as_mut_ptr()
194                                    .copy_from_nonoverlapping(data, len as usize);
195                            }
196
197                            u32::from_le_bytes(value)
198                        };
199
200                        ud.borrow_mut().device.write32(addr, to_write)?;
201                    } else {
202                        unsafe {
203                            ud.borrow_mut()
204                                .write_block(addr, std::slice::from_raw_parts(data, len as usize))?
205                        };
206                    }
207                }
208            }
209        },
210        FnOptions::Noc(op) => match op {
211            all_smi_luwen_if::FnNoc::Read {
212                noc_id,
213                x,
214                y,
215                addr,
216                data,
217                len,
218            } => {
219                let mut reader = ud.borrow_mut();
220                let reader: &mut ExtendedPciDevice = &mut reader;
221
222                reader.device.noc_read(
223                    &reader.default_tlb,
224                    Tlb {
225                        local_offset: addr,
226                        x_end: x as u8,
227                        y_end: y as u8,
228                        noc_sel: noc_id,
229                        mcast: false,
230                        ..Default::default()
231                    },
232                    unsafe { std::slice::from_raw_parts_mut(data, len as usize) },
233                )?;
234            }
235            all_smi_luwen_if::FnNoc::Write {
236                noc_id,
237                x,
238                y,
239                addr,
240                data,
241                len,
242            } => {
243                let mut writer = ud.borrow_mut();
244                let writer: &mut ExtendedPciDevice = &mut writer;
245
246                writer.device.noc_write(
247                    &writer.default_tlb,
248                    Tlb {
249                        local_offset: addr,
250                        x_end: x as u8,
251                        y_end: y as u8,
252                        noc_sel: noc_id,
253                        mcast: false,
254                        ..Default::default()
255                    },
256                    unsafe { std::slice::from_raw_parts(data, len as usize) },
257                )?;
258            }
259            all_smi_luwen_if::FnNoc::Broadcast {
260                noc_id,
261                addr,
262                data,
263                len,
264            } => {
265                let mut writer = ud.borrow_mut();
266                let writer: &mut ExtendedPciDevice = &mut writer;
267
268                let (x_start, y_start) = match writer.device.arch {
269                    all_smi_luwen_core::Arch::Grayskull => (0, 0),
270                    all_smi_luwen_core::Arch::Wormhole => (1, 0),
271                    all_smi_luwen_core::Arch::Blackhole => (0, 1),
272                };
273
274                writer.device.noc_write(
275                    &writer.default_tlb,
276                    Tlb {
277                        local_offset: addr,
278                        x_start,
279                        y_start,
280                        x_end: writer.grid_size_x - 1,
281                        y_end: writer.grid_size_y - 1,
282                        noc_sel: noc_id,
283                        mcast: true,
284                        ..Default::default()
285                    },
286                    unsafe { std::slice::from_raw_parts(data, len as usize) },
287                )?;
288            }
289            all_smi_luwen_if::FnNoc::Multicast {
290                noc_id,
291                start_x,
292                start_y,
293                end_x,
294                end_y,
295                addr,
296                data,
297                len,
298            } => {
299                let mut writer = ud.borrow_mut();
300                let writer: &mut ExtendedPciDevice = &mut writer;
301
302                let (min_start_x, min_start_y) = match writer.device.arch {
303                    all_smi_luwen_core::Arch::Grayskull => (0, 0),
304                    all_smi_luwen_core::Arch::Wormhole => (1, 0),
305                    all_smi_luwen_core::Arch::Blackhole => (0, 1),
306                };
307
308                let (start_x, start_y) = (start_x.max(min_start_x), start_y.max(min_start_y));
309
310                writer.device.noc_write(
311                    &writer.default_tlb,
312                    Tlb {
313                        local_offset: addr,
314                        x_start: start_x,
315                        y_start: start_y,
316                        x_end: end_x,
317                        y_end: end_y,
318                        noc_sel: noc_id,
319                        mcast: true,
320                        ..Default::default()
321                    },
322                    unsafe { std::slice::from_raw_parts(data, len as usize) },
323                )?;
324            }
325        },
326        FnOptions::Eth(op) => match op.rw {
327            all_smi_luwen_if::FnNoc::Read {
328                noc_id,
329                x,
330                y,
331                addr,
332                data,
333                len,
334            } => {
335                let mut borrow = ud.borrow_mut();
336                let borrow: &mut ExtendedPciDevice = &mut borrow;
337
338                let eth_x = borrow.eth_x;
339                let eth_y = borrow.eth_y;
340
341                let command_q_addr = borrow.device.noc_read32(
342                    &borrow.default_tlb,
343                    Tlb {
344                        local_offset: 0x170,
345                        noc_sel: 0,
346                        x_end: eth_x,
347                        y_end: eth_y,
348                        ..Default::default()
349                    },
350                )?;
351                let fake_block = borrow.fake_block;
352
353                let default_tlb = &mut borrow.default_tlb;
354                let read32 = |borrow: &mut PciDevice, addr| {
355                    borrow.noc_read32(
356                        default_tlb,
357                        Tlb {
358                            local_offset: addr,
359                            noc_sel: 0,
360                            x_end: eth_x,
361                            y_end: eth_y,
362                            ..Default::default()
363                        },
364                    )
365                };
366
367                let write32 = |borrow: &mut PciDevice, addr, data| {
368                    borrow.noc_write32(
369                        default_tlb,
370                        Tlb {
371                            local_offset: addr,
372                            noc_sel: 0,
373                            x_end: eth_x,
374                            y_end: eth_y,
375                            ..Default::default()
376                        },
377                        data,
378                    )
379                };
380
381                let dma_buffer = {
382                    let key = (eth_x, eth_y);
383                    if let Entry::Vacant(e) = borrow.ethernet_dma_buffer.entry(key) {
384                        // 1 MB buffer
385                        e.insert(borrow.device.allocate_dma_buffer(1 << 20)?);
386                    }
387
388                    // SAFETY: Can never get here without first inserting something into the hashmap
389                    unsafe { borrow.ethernet_dma_buffer.get_mut(&key).unwrap_unchecked() }
390                };
391
392                ethernet::fixup_queues(&mut borrow.device, read32, write32, command_q_addr)?;
393
394                if len <= 4 {
395                    let value = ethernet::eth_read32(
396                        &mut borrow.device,
397                        read32,
398                        write32,
399                        command_q_addr,
400                        EthCommCoord {
401                            coord: op.addr,
402                            noc_id,
403                            noc_x: x as u8,
404                            noc_y: y as u8,
405                            offset: addr,
406                        },
407                        std::time::Duration::from_secs(5 * 60),
408                    )?;
409
410                    let sl = unsafe { std::slice::from_raw_parts_mut(data, len as usize) };
411                    let vl = value.to_le_bytes();
412
413                    for (s, v) in sl.iter_mut().zip(vl.iter()) {
414                        *s = *v;
415                    }
416                } else {
417                    ethernet::block_read(
418                        &mut borrow.device,
419                        read32,
420                        write32,
421                        dma_buffer,
422                        command_q_addr,
423                        std::time::Duration::from_secs(5 * 60),
424                        fake_block,
425                        EthCommCoord {
426                            coord: op.addr,
427                            noc_id,
428                            noc_x: x as u8,
429                            noc_y: y as u8,
430                            offset: addr,
431                        },
432                        unsafe { std::slice::from_raw_parts_mut(data, len as usize) },
433                    )?;
434                }
435            }
436            all_smi_luwen_if::FnNoc::Write {
437                noc_id,
438                x,
439                y,
440                addr,
441                data,
442                len,
443            } => {
444                let mut borrow = ud.borrow_mut();
445                let borrow: &mut ExtendedPciDevice = &mut borrow;
446
447                let eth_x = borrow.eth_x;
448                let eth_y = borrow.eth_y;
449
450                let command_q_addr = borrow.device.noc_read32(
451                    &borrow.default_tlb,
452                    Tlb {
453                        local_offset: 0x170,
454                        noc_sel: 0,
455                        x_end: eth_x,
456                        y_end: eth_y,
457                        ..Default::default()
458                    },
459                )?;
460                let fake_block = borrow.fake_block;
461
462                let default_tlb = &borrow.default_tlb;
463                let read32 = |borrow: &mut PciDevice, addr| {
464                    borrow.noc_read32(
465                        default_tlb,
466                        Tlb {
467                            local_offset: addr,
468                            noc_sel: 0,
469                            x_end: eth_x,
470                            y_end: eth_y,
471                            ..Default::default()
472                        },
473                    )
474                };
475
476                let write32 = |borrow: &mut PciDevice, addr, data| {
477                    borrow.noc_write32(
478                        default_tlb,
479                        Tlb {
480                            local_offset: addr,
481                            noc_sel: 0,
482                            x_end: eth_x,
483                            y_end: eth_y,
484                            ..Default::default()
485                        },
486                        data,
487                    )
488                };
489
490                let dma_buffer = {
491                    let key = (eth_x, eth_y);
492                    if let Entry::Vacant(e) = borrow.ethernet_dma_buffer.entry(key) {
493                        // 1 MB buffer
494                        e.insert(borrow.device.allocate_dma_buffer(1 << 20)?);
495                    }
496
497                    // SAFETY: Can never get here without first inserting something into the hashmap
498                    unsafe { borrow.ethernet_dma_buffer.get_mut(&key).unwrap_unchecked() }
499                };
500
501                ethernet::fixup_queues(&mut borrow.device, read32, write32, command_q_addr)?;
502
503                if len <= 4 {
504                    let sl = unsafe { std::slice::from_raw_parts(data, len as usize) };
505                    let mut value = 0u32;
506                    for s in sl.iter().rev() {
507                        value <<= 8;
508                        value |= *s as u32;
509                    }
510
511                    ethernet::eth_write32(
512                        &mut borrow.device,
513                        read32,
514                        write32,
515                        command_q_addr,
516                        EthCommCoord {
517                            coord: op.addr,
518                            noc_id,
519                            noc_x: x as u8,
520                            noc_y: y as u8,
521                            offset: addr,
522                        },
523                        std::time::Duration::from_secs(5 * 60),
524                        value,
525                    )?;
526                } else {
527                    ethernet::block_write(
528                        &mut borrow.device,
529                        read32,
530                        write32,
531                        dma_buffer,
532                        command_q_addr,
533                        std::time::Duration::from_secs(5 * 60),
534                        fake_block,
535                        EthCommCoord {
536                            coord: op.addr,
537                            noc_id,
538                            noc_x: x as u8,
539                            noc_y: y as u8,
540                            offset: addr,
541                        },
542                        unsafe { std::slice::from_raw_parts(data, len as usize) },
543                    )?;
544                }
545            }
546            all_smi_luwen_if::FnNoc::Broadcast {
547                noc_id,
548                addr,
549                data,
550                len,
551            } => {
552                todo!("Tried to do an ethernet broadcast which is not supported, noc_id: {}, addr: {:#x}, data: {:p}, len: {:x}", noc_id, addr, data, len);
553            }
554            all_smi_luwen_if::FnNoc::Multicast {
555                noc_id,
556                start_x,
557                start_y,
558                end_x,
559                end_y,
560                addr,
561                data,
562                len,
563            } => {
564                todo!("Tried to do an ethernet multicast which is not supported, noc_id: {}, start: ({}, {}), end: ({}, {}), addr: {:#x}, data: {:p}, len: {:x}", noc_id, start_x, start_y, end_x, end_y, addr, data, len);
565            }
566        },
567    }
568
569    Ok(())
570}
571
572pub fn open(interface_id: usize) -> Result<all_smi_luwen_if::chip::Chip, LuwenError> {
573    let ud = ExtendedPciDevice::open(interface_id)?;
574
575    let arch = ud.borrow().device.arch;
576
577    Ok(all_smi_luwen_if::chip::Chip::open(
578        arch,
579        all_smi_luwen_if::CallbackStorage::new(comms_callback, ud.clone()),
580    )?)
581}