#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rio_lib_api.h" #include "dwl_fw.h" #define WRITE_DELAY_US 100000 #define WAITFLASH_DELAY_US 100000 #define MAX_RETRY_READ 1000 int srio_id = -1; #define MAX_BUFFER_SIZE (1024 * 1024) // 1MB #define MAX_NUMBERS (MAX_BUFFER_SIZE / 9) // circa 9 byte per numero: 8 hex + separatore constexpr size_t DWL_CHUNK_SIZE = 256; constexpr size_t DWL_WORDS = DWL_CHUNK_SIZE / sizeof(unsigned int); unsigned int dwl_buffer[DWL_WORDS]; unsigned int flashport; constexpr size_t ERASE_BLOCK_SIZE = 65536; int initSrio(int *srio_id) { const char* devFullPathName = "/dev/zynq_xmc_rio_0"; int openFlags = O_RDWR + O_NONBLOCK; // O_NONBLOCK makes the read operation not blocking when no data are available //int deviceFile; if (rio_ok != (rio_open(devFullPathName, srio_id))) { int err = errno; char buf[256]; snprintf (buf, sizeof(buf), "open(\"%s\", %d) returns FAIL (%s)\n", devFullPathName, openFlags, strerror(err)); perror (buf); exit(-err); return 0; } else { //rio_to_eth_mirror_set(1); return 1; } } bool parse_args(int argc, char** argv, uint16_t* endpoint, uint32_t* address, uint32_t* value) { if (argc != 4) { fprintf(stderr, "Uso: %s
\n", argv[0]); return false; } // Parsing dell'endpoint come uint16_t char* endptr = nullptr; unsigned long ep = strtoul(argv[1], &endptr, 0); if (*endptr != '\0' || ep > 0xFFFF) { fprintf(stderr, "Endpoint non valido: %s\n", argv[1]); return false; } *endpoint = static_cast(ep); // Parsing dell'address come uint32_t unsigned long addr = strtoul(argv[2], &endptr, 0); if (*endptr != '\0' || addr > 0xFFFFFFFF) { fprintf(stderr, "Address non valido: %s\n", argv[2]); return false; } *address = static_cast(addr); // Parsing del valore come uint32_t unsigned long val = strtoul(argv[3], &endptr, 0); if (*endptr != '\0' || val > 0xFFFFFFFF) { fprintf(stderr, "valore non valido: %s\n", argv[2]); return false; } *value = static_cast(val); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// BASE FUNCTION ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int srio_write_reg(uint16_t endpoint, uint32_t address, uint32_t value, int max_retry = 500, int timeout_us = 1000) //fatto { /*printf("Endpoint: 0x%04x\n", endpoint); printf("Address: 0x%08x\n", address); printf("Value: %d\n", value);*/ rio_result res; uint32_t address_offset = address & 0x000000FF; if (address_offset % 4) { printf("\nAddress 0x%08X non è multiplo di 4",address); return INPUT_NOT_VALID; } uint32_t buffer_size = 256/sizeof(unsigned int); unsigned int* buffer = (unsigned int*)malloc(256); if (!buffer) { perror("Errore nell'allocazione del buffer"); free(buffer); return GENERIC_ERROR; } uint32_t address_aligned = address & 0xFFFFFF00; res = rio_nread(srio_id, endpoint, address_aligned, buffer, buffer_size*4); if (res != rio_ok) { printf("\nErrore nella lettura da SRIO %d",res); free(buffer); return SRIO_READ_ERROR; } int i; for ( i = 0; i < max_retry; i++) { buffer[address_offset/4] = value; res = rio_nwrite( srio_id, //rio_id rid endpoint, //uint16_t remote_endpoint_id address_aligned, //void* remote_addr (the virtual machine is a 64bit operating system) buffer, //void* sending_payload buffer_size*4); //numCount*sizeof(uint32_t)); //size_t payload_size if (res != rio_ok) { printf("\nError writing to srio res=%d",res); free(buffer); return SRIO_WRITE_ERROR; } if(address == TX_FIFO_REG) break; res = rio_nread(srio_id, endpoint, address_aligned, buffer, buffer_size*4); if (res != rio_ok) { printf("\nErrore nella lettura da SRIO %d",res); free(buffer); return SRIO_READ_ERROR; } if(buffer[address_offset/4] == value) break; usleep(timeout_us); } if(i == max_retry) printf("\nsrio_write_reg:: FAIL (max retry) address 0x%08X, value 0x%08X\n",address, value); // Cleanup free(buffer); return NO_ERROR; } int srio_wait_reg( uint16_t endpoint, //fatto uint32_t address, uint32_t pos, uint32_t value, int max_retry = 500, int timeout_us = 1000) { /*printf("Endpoint: 0x%04x\n", endpoint); printf("Address: 0x%08x\n", address); printf("Value: %d\n", value);*/ uint32_t address_offset = address & 0x000000FF; if (address_offset % 4) { printf("\nAddress 0x%08X non è multiplo di 4",address); return INPUT_NOT_VALID; } uint32_t buffer_size = 256/sizeof(unsigned int); unsigned int* buffer = (unsigned int*)malloc(256); if (!buffer) { perror("Errore nell'allocazione del buffer"); free(buffer); return GENERIC_ERROR; } uint32_t address_aligned = address & 0xFFFFFF00; int i; int res = NO_ERROR; rio_result res_srio; for ( i = 0; i < max_retry; i++) { res_srio = rio_nread(srio_id, endpoint, address_aligned, buffer, buffer_size*4); if (res_srio != rio_ok) { printf("\nErrore nella lettura da SRIO %d",res); free(buffer); return SRIO_READ_ERROR; } if (( (buffer[address_offset/4]) >> pos & 0x00000001 ) == value) { //printf("\nreg: 0x%08X, pos: %d, value: %d. (wait finish)\n",address, pos, value); break; } else { //printf("\nWarning: SRIO WAIT: reg: 0x%08X, pos: %d, value: %d\n",address, pos, value); if ( address == STATUS_REG && pos == 12 && value == 0) { srio_write_reg(endpoint, CTRL_REG, 0x01); } res = SRIO_WAIT_ERROR; } usleep(timeout_us); } if (i == max_retry) { //printf("\nwait_flash exit: FAILED for max retry: %d\n",i); if (address != RX_FIFO_REG) { printf("\nwait_flash exit: FAILED for max retry: %d\n",i); printf("\nWarning: SRIO WAIT: reg: 0x%08X, pos: %d, value_expected: %d\n",address, pos, value); printf("\nPROGRAM STOPPED !\n"); exit(1); } } //else //printf("\nwait_flash exit: retry: %d\n",i); free(buffer); return res; } int srio_write_data(uint16_t endpoint, uint32_t address, uint32_t *data, uint32_t byte) //fatto (per ora scrive una rampa ogni 256 byte) { /*printf("Endpoint: 0x%04x\n", endpoint); printf("Address: 0x%08x\n", address); printf("Value: %d\n", value);*/ rio_result res; uint32_t address_offset = address & 0x000000FF; if (address_offset) { printf("\nsrio_write_data: Address 0x%08X non è multiplo di 256 byte",address); return INPUT_NOT_VALID; } uint32_t buffer_size = 256/sizeof(unsigned int); unsigned int* buffer; buffer = (unsigned int*)malloc(256); if (!buffer) { perror("Errore nell'allocazione del buffer"); free(buffer); return GENERIC_ERROR; } if(data == NULL) { for (int i = 0; i < byte/4; i++) { buffer[i] = i; } //NWRITE_SRIO_TX res = rio_nwrite( srio_id, //rio_id rid endpoint, //uint16_t remote_endpoint_id address, //void* remote_addr (the virtual machine is a 64bit operating system) buffer, //void* sending_payload buffer_size*4); //numCount*sizeof(uint32_t)); //size_t payload_size } else { res = rio_nwrite( srio_id, //rio_id rid endpoint, //uint16_t remote_endpoint_id address, //void* remote_addr (the virtual machine is a 64bit operating system) data, //void* sending_payload buffer_size*4); //numCount*sizeof(uint32_t)); //size_t payload_size } if (res != rio_ok) { printf("\nError writing to srio res=%d",res); free(buffer); return SRIO_WRITE_ERROR; } // Cleanup free(buffer); return NO_ERROR; } int srio_read_reg(uint16_t endpoint, uint32_t address, uint32_t byte) //fatto { /*printf("Endpoint: 0x%04x\n", endpoint); printf("Address: 0x%08x\n", address); printf("byte: %d\n", byte);*/ rio_result res; uint32_t address_offset = address & 0x000000FF; if (address_offset % 4) { printf("\nAddress 0x%08X non è multiplo di 4",address); return INPUT_NOT_VALID; } uint32_t buffer_size = 256/sizeof(unsigned int); unsigned int* buffer = (unsigned int*)malloc(256); if (!buffer) { perror("Errore nell'allocazione del buffer"); free(buffer); return GENERIC_ERROR; } uint32_t address_aligned = address & 0xFFFFFF00; res = rio_nread(srio_id, endpoint, address_aligned, buffer, buffer_size*4); if (res != rio_ok) { printf("\nErrore nella lettura da SRIO %d",res); free(buffer); return SRIO_READ_ERROR; } printf("\nRead Value: \n"); for (int i = 0; i < buffer_size; i=i+4) { printf("0x%08X 0x%08X 0x%08X 0x%08X \n", buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]); } // Cleanup free(buffer); return NO_ERROR; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////// MAIN MACRO //////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* ./SRIOwrite < SRIO ID > CTRL_REG 0x01 # aspetta che il bit#11 di STATUS_REG va a 1 */ int start_request(uint16_t endpoint){ //fatto srio_write_reg(endpoint, CTRL_REG, 0x01); srio_wait_reg(endpoint,STATUS_REG,11,1); return NO_ERROR; } /* # controlla che i bit #1 #4 #5 #6 #10 di STATUS_REG sono 0 altrimenti ferma tutto e riporta errore ./SRIOwrite < SRIO ID > CTRL_REG 0x00 # aspetta che il bit#11 di STATUS_REG va a 0 */ int end_request(uint16_t endpoint){ //fatto srio_wait_reg(endpoint,STATUS_REG,1,0); srio_wait_reg(endpoint,STATUS_REG,4,0); srio_wait_reg(endpoint,STATUS_REG,5,0); srio_wait_reg(endpoint,STATUS_REG,6,0); srio_wait_reg(endpoint,STATUS_REG,10,0); srio_write_reg(endpoint, CTRL_REG, 0x00); srio_wait_reg(endpoint,STATUS_REG,11,0); return NO_ERROR; } /* ./SRIOwrite < SRIO ID > CTRL_REG 0x03 # aspetta che il bit#12 di STATUS_REG va a 1 ./SRIOwrite < SRIO ID > CTRL_REG 0x01 # aspetta che il bit#12 di STATUS_REG va a 0 END_REQUEST */ int write_data(uint16_t endpoint){ // fatto srio_write_reg(endpoint, CTRL_REG, 0x03); srio_wait_reg(endpoint,STATUS_REG,12,1); srio_write_reg(endpoint, CTRL_REG, 0x01); srio_wait_reg(endpoint,STATUS_REG,12,0); end_request(endpoint); return NO_ERROR; } /* START_REQUEST ./SRIOwrite < SRIO ID > CTRL_REG 0x03 # aspetta che il bit#12 di STATUS_REG va a 1 ./SRIOwrite < SRIO ID > CTRL_REG 0x01 # aspetta che il bit#12 di STATUS_REG va a 0 */ int read_data(uint16_t endpoint){ //fatto start_request(endpoint); srio_write_reg(endpoint, CTRL_REG, 0x03); srio_wait_reg(endpoint,STATUS_REG,12,1); srio_write_reg(endpoint, CTRL_REG, 0x01); srio_wait_reg(endpoint,STATUS_REG,12,0); return NO_ERROR; } /* START_REQUEST ./SRIOwrite < SRIO ID > CTRL_REG 0x03 # aspetta che il bit#12 di STATUS_REG va a 1 ./SRIOwrite < SRIO ID > CTRL_REG 0x01 # aspetta che il bit#12 di STATUS_REG va a 0 END_REQUEST */ int send_request(uint16_t endpoint){ //fatto start_request(endpoint); srio_write_reg(endpoint, CTRL_REG, 0x03); srio_wait_reg(endpoint,STATUS_REG,12,1); srio_write_reg(endpoint, CTRL_REG, 0x01); srio_wait_reg(endpoint,STATUS_REG,12,0); end_request(endpoint); return NO_ERROR; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// GENERAL FUNCTIONS ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* ./SRIOwrite < SRIO ID > MODE_REG 0x00 ./SRIOwrite < SRIO ID > CMD_REG 0x06 SEND_REQUEST ./SRIOwrite < SRIO ID > MODE_REG 0x00 ./SRIOwrite < SRIO ID > CMD_REG 0x61 START_REQUEST ./SRIOwrite < SRIO ID > TX_FIFO_REG 0x6F WRITE_DATA ./SRIOwrite < SRIO ID > MODE_REG 0x02 ./SRIOwrite < SRIO ID > CMD_REG 0x06 SEND_REQUEST ./SRIOwrite < SRIO ID > MODE_REG 0x02 ./SRIOwrite < SRIO ID > CMD_REG 0xB7 */ int op_init_qspi(uint16_t endpoint) //fatto { printf("\nop_init_qspi endpoint: 0x%04X\n", endpoint); srio_write_reg(endpoint, MODE_REG, 0x00 + flashport); srio_write_reg(endpoint, CMD_REG, 0x06); send_request(endpoint); srio_write_reg(endpoint, MODE_REG, 0x00 + flashport); srio_write_reg(endpoint, CMD_REG, 0x61); start_request(endpoint); srio_write_reg(endpoint, TX_FIFO_REG, 0x6F); write_data(endpoint); srio_write_reg(endpoint, MODE_REG, 0x02 + flashport); srio_write_reg(endpoint, CMD_REG, 0x06); send_request(endpoint); srio_write_reg(endpoint, MODE_REG, 0x02 + flashport); srio_write_reg(endpoint, CMD_REG, 0xB7); send_request(endpoint); return NO_ERROR; } /* ./SRIOwrite < SRIO ID > MODE_REG 0x06 ./SRIOwrite < SRIO ID > ADDR_REG < MEMORY ADDRESS > ./SRIOwrite < SRIO ID > NUM_BYTE_REG < NUM BYTE TO READ > ./SRIOwrite < SRIO ID > CMD_REG 0x0B READ_DATA ./SRIOread < SRIO ID > RX_FIFO_REG < DATA TO READ > END_REQUEST */ int read_flash(uint16_t endpoint, uint32_t address, uint32_t byte) //fatto { printf("\nop_init_qspi endpoint: 0x%04X, address 0x%08X, byte: %d \n", endpoint, address, byte); srio_write_reg(endpoint, MODE_REG, 0x06 + flashport); srio_write_reg(endpoint, ADDR_REG, address); srio_write_reg(endpoint, NUM_BYTE_REG, byte); srio_write_reg(endpoint, CMD_REG, 0x0B); read_data(endpoint); srio_read_reg(endpoint, RX_FIFO_REG, byte); end_request(endpoint); return NO_ERROR; } /* ./SRIOwrite < SRIO ID > MODE_REG 0x06 ./SRIOwrite < SRIO ID > CMD_REG 0x05 READ_DATA # aspetta che il bit#0 di RX_FIFO_REG va a 0 END_REQUEST */ int wait_flash(uint16_t endpoint) //fatto { int res = NO_ERROR; //printf("\nwait_flash...\n"); res += srio_write_reg(endpoint, MODE_REG, 0x06 + flashport); res += srio_write_reg(endpoint, CMD_REG, 0x05); res += read_data(endpoint); res += srio_wait_reg(endpoint,RX_FIFO_REG,0,0); res += end_request(endpoint); return res; } int wait_flash_with_retry(uint16_t endpoint,int max_retry, int timeout_us) { int res = NO_ERROR; int i; for ( i = 0; i < max_retry; i++) { res = wait_flash(endpoint); if(res == NO_ERROR) break; usleep(timeout_us); } if (i == max_retry) printf("\nwait_flash exit: FAILED for max retry: %d\n",i); //else printf("\nwait_flash exit: retry: %d\n",i); return res; } /* ./SRIOwrite < SRIO ID > MODE_REG 0x02 ./SRIOwrite < SRIO ID > CMD_REG 0x06 SEND_REQUEST ./SRIOwrite < SRIO ID > MODE_REG 0x06 ./SRIOwrite < SRIO ID > ADDR_REG < MEMORY ADDRESS > ./SRIOwrite < SRIO ID > NUM_BYTE_REG < NUM BYTE TO WRITE > ./SRIOwrite < SRIO ID > CMD_REG 0x02 START_REQUEST ./SRIOwrite < SRIO ID > TX_FIFO_REG < DATA TO WRITE > # Per esempio: # ./SRIOwrite < SRIO ID > TX_FIFO_REG 0x12345678 0xAABBCCDD 0x00112233 0x24681357 0x12345678 0xAABBCCDD 0x00112233 0x24681357 WRITE_DATA */ int write_flash(uint16_t endpoint, uint32_t address, uint32_t *data, uint32_t byte) //fatto { //printf("\nwrite_flash endpoint: 0x%04X, address 0x%08X, byte: %d \n", endpoint, address, byte); srio_write_reg(endpoint, MODE_REG, 0x02 + flashport); srio_write_reg(endpoint, CMD_REG, 0x06); send_request(endpoint); srio_write_reg(endpoint, MODE_REG, 0x06 + flashport); srio_write_reg(endpoint, ADDR_REG, address); srio_write_reg(endpoint, NUM_BYTE_REG, byte); srio_write_reg(endpoint, CMD_REG, 0x02); start_request(endpoint); srio_write_data(endpoint, TX_FIFO_REG, data, 256); write_data(endpoint); return NO_ERROR; } /* ./SRIOwrite < SRIO ID > MODE_REG 0x02 ./SRIOwrite < SRIO ID > CMD_REG 0x06 SEND_REQUEST ./SRIOwrite < SRIO ID > MODE_REG 0x06 ./SRIOwrite < SRIO ID > ADDR_REG < MEMORY ADDRESS > ./SRIOwrite < SRIO ID > NUM_BYTE_REG 0x00 ./SRIOwrite < SRIO ID > CMD_REG 0xD8 SEND_REQUEST */ int erase_section(uint16_t endpoint, uint32_t address) //fatto { printf("\nerase_section endpoint: 0x%04X, address 0x%08X\n", endpoint, address); srio_write_reg(endpoint, MODE_REG, 0x02 + flashport); srio_write_reg(endpoint, CMD_REG, 0x06); send_request(endpoint); srio_write_reg(endpoint, MODE_REG, 0x06 + flashport); srio_write_reg(endpoint, ADDR_REG, address); srio_write_reg(endpoint, NUM_BYTE_REG, 0x00); srio_write_reg(endpoint, CMD_REG, 0xD8); send_request(endpoint); return NO_ERROR; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { uint16_t endpoint; uint32_t address; uint32_t value; rio_result res; if (!parse_args(argc, argv, &endpoint, &address, &value)) { return 1; // errore nei parametri } printf("Endpoint: 0x%04x\n", endpoint); printf("Address: 0x%08x\n", address); printf("Value: %d\n", value); initSrio(&srio_id); /* erase_section(endpoint, 0x01F00000); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); erase_section(endpoint, 0x01F10000); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); erase_section(endpoint, 0x01F20000); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); write_flash(endpoint,0x01F00000,NULL,256); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); write_flash(endpoint,0x01F00100,NULL,256); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); write_flash(endpoint,0x01F00200,NULL,256); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); read_flash(endpoint, 0x01F00000, 256); read_flash(endpoint, 0x01F00100, 256); read_flash(endpoint, 0x01F00200, 256); */ std::cout << "insert download file: "<< std::endl; std::string filename; std::streamsize size = 0; std::getline(std::cin, filename); std::ifstream file(filename,std::ios::binary | std::ios::ate); if (!file) { std::cerr <<"impossibile aprire il file\n"; close(srio_id); return 1; } else { size = file.tellg(); std::cout << "File Dimension: " << size << "bytes\n"; } std::cout << "insert flash number ( primary : 0 , secondary : 1): "; std::string line; std::getline(std::cin,line); try { flashport = std::stoul(line, nullptr,10); if(flashport !=0 && flashport !=1) { std::cerr << "Flash number errato!" << '\n'; close(srio_id); file.close(); exit(1); } } catch(...) { std::cerr << "Numero non valido!" << '\n'; close(srio_id); file.close(); } if(flashport == 1) flashport = 8; std::cout << "insert flash address (fomato esadecimale senza 0x): "; //std::string line; std::getline(std::cin,line); unsigned int start_address_to_dwl; try { start_address_to_dwl = std::stoul(line, nullptr,16); } catch(...) { std::cerr << "Numero esadecimale non valido!" << '\n'; close(srio_id); file.close(); } printf("\nIndirizzo inserito: 0x%08X\n",start_address_to_dwl); std::cout << "Procediamo? (Ctrl+C per annullare)"; std::getline(std::cin,line); op_init_qspi(endpoint); op_init_qspi(endpoint); read_flash(endpoint, address, 256); std::cout << "Procediamo? (Ctrl+C per annullare)"; std::getline(std::cin,line); //ERASE FULL SECTION std::cout << "\nErasing memory...\n\n"; int i; unsigned long start_address_to_erase = start_address_to_dwl; for ( i = 0; i < size; i = i + ERASE_BLOCK_SIZE) { erase_section(endpoint, start_address_to_erase); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); start_address_to_erase += ERASE_BLOCK_SIZE; if (start_address_to_erase > start_address_to_dwl+size) start_address_to_erase = start_address_to_dwl+size; std::cout << "\rErasing.. " << i << " / " << size << " byte" << std::flush; } std::cout << "Procediamo? (Ctrl+C per annullare)"; std::getline(std::cin,line); //WRITE DATA std::cout << "\nWriting memory...\n\n"; file.seekg(0, std::ios::beg); //puntiamo all'inizio del file address = start_address_to_dwl; std::streamsize bytes_read = 0; std::streamsize bytes_written = 0; while (file) { file.read(reinterpret_cast(dwl_buffer), DWL_CHUNK_SIZE); bytes_read = file.gcount(); if (bytes_read == 0) break; // Se l’ultimo blocco è < 256 byte, padding a zero if (bytes_read < DWL_CHUNK_SIZE) { std::memset( reinterpret_cast(dwl_buffer) + bytes_read, 0, DWL_CHUNK_SIZE - bytes_read ); } write_flash(endpoint, address, dwl_buffer, DWL_CHUNK_SIZE); usleep(WRITE_DELAY_US*value); wait_flash_with_retry(endpoint, MAX_RETRY_READ, 1000); usleep(WAITFLASH_DELAY_US*value); address += DWL_CHUNK_SIZE; bytes_written += DWL_CHUNK_SIZE; if (bytes_written > size) bytes_written = size; std::cout << "\rWriting " << bytes_written << " / " << size << " byte" << " address: " << std::hex << address << std::flush; } std::cout << std::endl; file.close(); close(srio_id); return 0; } /* per tutte le altre "aspetta" bisogna rileggere lo stesso registro con nread, non rifacendo tutta la macro mi devo fermare subito se non va mai al valore nell'endrequest la "controlla" controlla solamente e se c'è qualche errore si blocca tutto */