|
| 1 | +// Amazon FPGA Hardware Development Kit |
| 2 | +// |
| 3 | +// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 4 | +// |
| 5 | +// Licensed under the Amazon Software License (the "License"). You may not use |
| 6 | +// this file except in compliance with the License. A copy of the License is |
| 7 | +// located at |
| 8 | +// |
| 9 | +// http://aws.amazon.com/asl/ |
| 10 | +// |
| 11 | +// or in the "license" file accompanying this file. This file is distributed on |
| 12 | +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or |
| 13 | +// implied. See the License for the specific language governing permissions and |
| 14 | +// limitations under the License. |
| 15 | +#include <stdio.h> |
| 16 | +#include <stdint.h> |
| 17 | +#include <stdbool.h> |
| 18 | +#include <stdarg.h> |
| 19 | + |
| 20 | +#include <utils/sh_dpi_tasks.h> |
| 21 | + |
| 22 | +#include "common_dma.h" |
| 23 | + |
| 24 | +static uint16_t pci_vendor_id = 0x1D0F; /* Amazon PCI Vendor ID */ |
| 25 | +static uint16_t pci_device_id = 0xF001; |
| 26 | + |
| 27 | +void usage(const char* program_name) { |
| 28 | + printf("usage: %s [--slot <slot>]\n", program_name); |
| 29 | +} |
| 30 | + |
| 31 | +/* helper function to initialize a buffer that would be written to the FPGA later */ |
| 32 | +void |
| 33 | +rand_string(char *str, size_t size) |
| 34 | +{ |
| 35 | + static const char charset[] = |
| 36 | + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTSUVWXYZ1234567890"; |
| 37 | + static bool seeded = false; |
| 38 | + int i; |
| 39 | + |
| 40 | + if (!seeded) { |
| 41 | + srand(time(NULL)); |
| 42 | + seeded = true; |
| 43 | + } |
| 44 | + |
| 45 | + for(i = 0; i < size; ++i) { |
| 46 | + unsigned int key = rand() % (sizeof charset - 1); |
| 47 | + str[i] = charset[key]; |
| 48 | + } |
| 49 | + |
| 50 | + str[size-1] = '\0'; |
| 51 | +} |
| 52 | + |
| 53 | +#ifndef SV_TEST |
| 54 | + |
| 55 | +static int |
| 56 | +check_slot_config(int slot_id) |
| 57 | +{ |
| 58 | + int rc; |
| 59 | + struct fpga_mgmt_image_info info = {0}; |
| 60 | + |
| 61 | + /* get local image description, contains status, vendor id, and device id */ |
| 62 | + rc = fpga_mgmt_describe_local_image(slot_id, &info, 0); |
| 63 | + fail_on(rc, out, "Unable to get local image information. Are you running as root?"); |
| 64 | + |
| 65 | + /* check to see if the slot is ready */ |
| 66 | + if (info.status != FPGA_STATUS_LOADED) { |
| 67 | + rc = 1; |
| 68 | + fail_on(rc, out, "Slot %d is not ready", slot_id); |
| 69 | + } |
| 70 | + |
| 71 | + /* confirm that the AFI that we expect is in fact loaded */ |
| 72 | + if (info.spec.map[FPGA_APP_PF].vendor_id != pci_vendor_id || |
| 73 | + info.spec.map[FPGA_APP_PF].device_id != pci_device_id) { |
| 74 | + rc = 1; |
| 75 | + printf("The slot appears loaded, but the pci vendor or device ID doesn't " |
| 76 | + "match the expected values. You may need to rescan the fpga with \n" |
| 77 | + "fpga-describe-local-image -S %i -R\n" |
| 78 | + "Note that rescanning can change which device file in /dev/ a FPGA will map to.\n" |
| 79 | + "To remove and re-add your edma driver and reset the device file mappings, run\n" |
| 80 | + "`sudo rmmod edma-drv && sudo insmod <aws-fpga>/sdk/linux_kernel_drivers/edma/edma-drv.ko`\n", |
| 81 | + slot_id); |
| 82 | + fail_on(rc, out, "The PCI vendor id and device of the loaded image are " |
| 83 | + "not the expected values."); |
| 84 | + } |
| 85 | + |
| 86 | +out: |
| 87 | + return rc; |
| 88 | +} |
| 89 | + |
| 90 | +int open_dma_queue(int slot_id) |
| 91 | +{ |
| 92 | + int fd, rc; |
| 93 | + char device_file_name[256]; |
| 94 | + |
| 95 | + rc = sprintf(device_file_name, "/dev/edma%i_queue_0", slot_id); |
| 96 | + fail_on((rc = (rc < 0)? 1:0), out, "Unable to format device file name."); |
| 97 | + printf("device_file_name=%s\n", device_file_name); |
| 98 | + |
| 99 | + /* make sure the AFI is loaded and ready */ |
| 100 | + rc = check_slot_config(slot_id); |
| 101 | + fail_on(rc, out, "slot config is not correct"); |
| 102 | + |
| 103 | + fd = open(device_file_name, O_RDWR); |
| 104 | + if(fd<0){ |
| 105 | + printf("Cannot open device file %s.\nMaybe the EDMA " |
| 106 | + "driver isn't installed, isn't modified to attach to the PCI ID of " |
| 107 | + "your CL, or you're using a device file that doesn't exist?\n" |
| 108 | + "See the edma_install manual at <aws-fpga>/sdk/linux_kernel_drivers/edma/edma_install.md\n" |
| 109 | + "Remember that rescanning your FPGA can change the device file.\n" |
| 110 | + "To remove and re-add your edma driver and reset the device file mappings, run\n" |
| 111 | + "`sudo rmmod edma-drv && sudo insmod <aws-fpga>/sdk/linux_kernel_drivers/edma/edma-drv.ko`\n", |
| 112 | + device_file_name); |
| 113 | + fail_on((rc = (fd < 0)? 1:0), out, "unable to open DMA queue. "); |
| 114 | + } |
| 115 | +out: |
| 116 | + return fd; |
| 117 | +} |
| 118 | + |
| 119 | +#endif |
| 120 | + |
| 121 | +int fpga_driver_write_buffer_to_cl(int slot_id, int channel, int fd, size_t buffer_size, size_t address){ |
| 122 | + int rc; |
| 123 | + size_t write_offset =0; |
| 124 | + |
| 125 | + while (write_offset < buffer_size) { |
| 126 | + if (write_offset != 0) { |
| 127 | + printf("Partial write by driver, trying again with remainder of buffer (%lu bytes)\n", |
| 128 | + buffer_size - write_offset); |
| 129 | + } |
| 130 | + rc = pwrite(fd, |
| 131 | + write_buffer + write_offset, |
| 132 | + buffer_size - write_offset, |
| 133 | + 0x10000000 + channel*MEM_16G + write_offset); |
| 134 | + if (rc < 0) { |
| 135 | + fail_on((rc = (rc < 0)? errno:0), out, "call to pwrite failed."); |
| 136 | + } |
| 137 | + write_offset += rc; |
| 138 | + } |
| 139 | + rc = 0; |
| 140 | +out: |
| 141 | + return rc; |
| 142 | +} |
| 143 | + |
| 144 | +int fpga_driver_read_cl_to_buffer(int slot_id, int channel, int fd, size_t buffer_size, size_t address){ |
| 145 | + size_t read_offset =0; |
| 146 | + int rc; |
| 147 | + while (read_offset < buffer_size) { |
| 148 | + if (read_offset != 0) { |
| 149 | + printf("Partial read by driver, trying again with remainder of buffer (%lu bytes)\n", |
| 150 | + buffer_size - read_offset); |
| 151 | + } |
| 152 | + rc = pread(fd, |
| 153 | + read_buffer + read_offset, |
| 154 | + buffer_size - read_offset, |
| 155 | + 0x10000000 + channel*MEM_16G + read_offset); |
| 156 | + if (rc < 0) { |
| 157 | + fail_on((rc = (rc < 0)? errno:0), out, "call to pread failed."); |
| 158 | + } |
| 159 | + read_offset += rc; |
| 160 | + } |
| 161 | + rc = 0; |
| 162 | +out: |
| 163 | + return rc; |
| 164 | +} |
| 165 | + |
| 166 | +void fpga_read_cl_to_buffer(int slot_id, int channel, int fd, size_t buffer_size, size_t address) { |
| 167 | +#ifdef SV_TEST |
| 168 | + sv_fpga_start_cl_to_buffer(slot_id, channel, buffer_size, address); |
| 169 | +#else |
| 170 | + fpga_driver_read_cl_to_buffer(slot_id, channel, fd, buffer_size, address); |
| 171 | +#endif |
| 172 | + dma_memcmp(buffer_size); |
| 173 | +} |
| 174 | + |
| 175 | +void fpga_write_buffer_to_cl(int slot_id, int channel, int fd, size_t buffer_size, size_t address){ |
| 176 | +#ifdef SV_TEST |
| 177 | + sv_fpga_start_buffer_to_cl(slot_id, channel, buffer_size, write_buffer, address); |
| 178 | +#else |
| 179 | + fpga_driver_write_buffer_to_cl(slot_id, channel, fd, buffer_size, address); |
| 180 | +#endif |
| 181 | +} |
| 182 | + |
| 183 | +int dma_memcmp (size_t buffer_size) { |
| 184 | + int rc = 0; |
| 185 | + if (memcmp(write_buffer, read_buffer, buffer_size) == 0) { |
| 186 | + printf("DRAM DMA read the same string as it wrote on channel %d (it worked correctly!)\n", channel); |
| 187 | + } else { |
| 188 | + int i; |
| 189 | + printf("Bytes written to channel %d:\n", channel); |
| 190 | + for (i = 0; i < buffer_size; ++i) { |
| 191 | + printf("%c", write_buffer[i]); |
| 192 | + } |
| 193 | + |
| 194 | + printf("\n\n"); |
| 195 | + |
| 196 | + printf("Bytes read:\n"); |
| 197 | + for (i = 0; i < buffer_size; ++i) { |
| 198 | + printf("%c", read_buffer[i]); |
| 199 | + } |
| 200 | + printf("\n\n"); |
| 201 | +#ifndef SV_TEST |
| 202 | + rc = 1; |
| 203 | + fail_on(rc, out, "Data read from DMA did not match data written with DMA. Was there an fsync() between the read and write?"); |
| 204 | +#else |
| 205 | + error_count++; |
| 206 | +#endif |
| 207 | + } |
| 208 | +out: |
| 209 | + return rc; |
| 210 | +} |
| 211 | + |
| 212 | +#ifdef SV_TEST |
| 213 | + |
| 214 | +int send_rdbuf_to_c(char* rd_buf) |
| 215 | + |
| 216 | +{ |
| 217 | +// Vivado does not support svGetScopeFromName |
| 218 | + #ifndef VIVADO_SIM |
| 219 | + svScope scope; |
| 220 | + scope = svGetScopeFromName("tb"); |
| 221 | + svSetScope(scope); |
| 222 | + #endif |
| 223 | + |
| 224 | + int i; |
| 225 | + |
| 226 | + //For Questa simulator the first 8 bytes are not transmitted correctly, so the buffer is transferred with 8 extra bytes and those bytes are removed here. Made this default for all the simulators. |
| 227 | + for (i = 0; i < buffer_size; ++i) { |
| 228 | + read_buffer[i] = rd_buf[i+8]; |
| 229 | + } |
| 230 | + |
| 231 | + //end of line character is not transferered correctly. So assign that here. |
| 232 | + read_buffer[buffer_size-1] = '\0'; |
| 233 | + |
| 234 | + return 0; |
| 235 | + |
| 236 | +} |
| 237 | + |
| 238 | +#endif |
| 239 | + |
| 240 | + |
| 241 | + |
0 commit comments